What are ways to update ASP.NET page from HttpModule? - asp.net

I need to update an ASP.NET page from the HttpModule permanently, or from time to time.
Here is the code of IUpdatablePage interface for our page to be updated:
interface IUpdatablePage
{
void Update( string value );
}
Here is the code of HttpModule, I imagine, can be:
void IHttpModule.Init( HttpApplication application )
{
application.PreRequestHandlerExecute += new EventHandler( application_PreRequestHandlerExecute );
}
void application_PreRequestHandlerExecute( object sender, EventArgs e )
{
this._Page = ( Page )HttpContext.Current.Handler;
}
void HttpModuleProcessing()
{
//... doing smth
IUpdatablePage page = this._Page as IUpdatablePage;
page.Update( currentVaue );
//... continue doing smth
}
Here we:
save the current request page in _Page,
get access to IUpdatablePage interface while processing in HttpModule
call Update function passing some currentValue.
Now the page gets the value in Update function.
public partial class MyPage: System.Web.Page, IUpdatablePage
{
void IUpdatablePage.Update( string value )
{
// Here we need to update the page with new value
Label1.Text = value;
}
}
The question is what are the ways to transmit this value to the webform controls so that they would immediately show it in browser?
I suppose any way of refreshing the page: using UpdatePanel, Timer, iframe block, javascript etc.
NOTE, that the request from the page is being processed in HttpModule while refresh.
Please, help with code samples (I'm a web-beginner).

The way to transfer data between Page and HttpModule is to use Application named static objects, that are identified by session id.
The page is update via UpdatePanel triggered by timer.
The code of HttpModule (simplified):
public class UploadProcessModule : IHttpModule
{
public void Init( HttpApplication context )
{
context.BeginRequest += context_BeginRequest;
}
void context_BeginRequest( object sender, EventArgs e )
{
HttpContext context = ( ( HttpApplication )sender ).Context;
if ( context.Request.Cookies["ASP.NET_SessionId"] != null )
{
string sessionId = context.Request.Cookies["ASP.NET_SessionId"].Value;
context.Application["Data_" + sessionId] = new MyClass();
}
}
}

Related

Call parent page function from user control

I have a Default.aspx page and I am using a usercontrol in it. On some condition in usercontrol.cs I have to invoke a function present in Default.aspx.cs page (i.e parent page of user control). Please help and tell me the way to do this task.
You have to cast the Page property to the actual type:
var def = this.Page as _Default;
if(def != null)
{
def.FunctionName();
}
the method must be public:
public partial class _Default : System.Web.UI.Page
{
public void FunctionName()
{
}
}
But note that this is not best-practise since you are hard-linking the UserControl with a Page. Normally one purpose of a UserControl is reusability. Not anymore here. The best way to communicate from a UserControl with it's page is using a custom event which can be handled by the page.
Mastering Page-UserControl Communication - event driven communication
Add an event to the user control:
public event EventHandler SpecialCondition;
Raise this event inside your user control when the condition is met:
private void RaiseSpecialCondition()
{
if (SpecialCondition != null) // If nobody subscribed to the event, it will be null.
SpecialCondition(this, EventArgs.Empty);
}
Then in your page containing the user control, listen for the event:
public partial class _Default : System.Web.UI.Page
{
public void Page_OnLoad(object sender, EventArgs e)
{
this.UserControl1.OnSpecialCondition += HandleSpecialCondition;
}
public void HandleSpecialCondition(object sender, EventArgs e)
{
// Your handler here.
}
}
You can change the EventArgs to something more useful to pass values around, if required.
parent.aspx.cs
public void DisplayMsg(string message)
{
if (message == "" || message == null) message = "Default Message";
Response.Write(message);
}
To Call function of parent Page from user control use the following:
UserControl.ascx.cs
this.Page.GetType().InvokeMember("DisplayMsg", System.Reflection.BindingFlags.InvokeMethod, null, this.Page, new object[] { "My Message" });
This works fine for me..
Try this
MyAspxClassName aspxobj= new MyUserControlClassName();
aspxobj.YourMethod(param);

can httphandler fire an event?

I want to check Session in some pages. To do this I am adding the page names which I want to check inside web.config as a appsetting key.
I want to use httpHandler with firing an event after it finds the session is empty or something else.
If I create httpHandler as a dll(another project) and add to a web site, can handler fire an event and web site capture it inside a web page?
What you can do is this:
Your HttpHandler puts a value in the HttpContext.Current.Items collection telling if there was Session or not. Something like
HttpContext.Current.Items.Add("SessionWasThere") = true;
You create a BasePage that checks that value in the Page_Load event and raises a new event telling so:
public abstract class BasePage : Page {
public event EventHandler NoSession;
protected override void OnLoad(EventArgs e){
var sessionWasThere = (bool)HttpContext.Current.Items.Add("SessionWasThere");
if(!sessionWasThere && NoSession != null)
NoSession(this, EventArgs.Empty);
}
}
In your page, you suscribe to that event:
public class MyPage : BasePage{
protected override void OnInit(){
NoSession += Page_NoSession;
}
private void Page_NoSession(object sender, EventArgs e) {
//...
}
}

ASP.NET MVC3 : Connected clients count

In my ASP.NET MVC3 web application I'd like to add a small message displaying the number of users currently browsing the site.
I'm currently using Session_Start and Session_End application events to increment or decrement a static property inside Global.asax.
This works, but it isn't precise at all. Since my session timeout is configured to 20mn there's a huge delay between updates.
Is there a more elegant, precise way of doing this?
I've thought of calling an action via AJAX which simply does Session.Abandon() on the window.onbeforeunload javascript event, but this would be called each time the user changed pages. Is there a way to determine when the user closes his browser or leaves the domain?
Any hints, comments or code examples would be welcome!
Here is the relevant part of current code:
public class MvcApplication : System.Web.HttpApplication
{
public static int UsersConnected { get; set; }
protected void Session_Start(object sender, EventArgs e)
{
Application.Lock();
UsersConnected++;
Application.UnLock();
}
protected void Session_End(object sender, EventArgs e)
{
Application.Lock();
UsersConnected--;
Application.UnLock();
}
...
}
Okay this was a bit tricky but I've come with an 'okay' solution.
First, Ive created a static dictionary in Global.asax which will store the IP address of the clients and their last poll date.
public class MvcApplication : System.Web.HttpApplication
{
public static Dictionary<string, DateTime> ConnectedtUsers { get; set; }
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ConnectedtUsers = new Dictionary<string, DateTime>();
}
...
}
Then, in my CommonsController, I've created this action which will add new clients, remove clients which haven't been polled in the last 30 seconds and update the poll date of already registered clients :
public class CommonsController : Controller
{
...
public JsonResult UserConnected()
{
string ip = Request.UserHostAddress;
if (MvcApplication.ConnectedtUsers.ContainsKey(ip))
{
MvcApplication.ConnectedtUsers[ip] = DateTime.Now;
}
else
{
MvcApplication.ConnectedtUsers.Add(ip, DateTime.Now);
}
int connected = MvcApplication.ConnectedtUsers.Where(c => c.Value.AddSeconds(30d) > DateTime.Now).Count();
foreach (string key in MvcApplication.ConnectedtUsers.Where(c => c.Value.AddSeconds(30d) < DateTime.Now).Select(c => c.Key))
{
MvcApplication.ConnectedtUsers.Remove(key);
}
return Json(new { count = connected }, JsonRequestBehavior.AllowGet);
}
}
Finally, on my layout page, added this code which will call my action every 30 seconds and output the result in a span :
<span id="connectedUsers"></span>
<script type="text/javascript">
function PollUsers()
{
$(function() {
$.getJSON("/Commons/UserConnected", function(json){ $('#connectedUsers').text(json.count + " user(s) connected")});
});
}
setInterval(PollUsers, 30000);
</script>
May not be that precise, maybe not that elegant either, but it works. Of course, multiple users from the same IP would count for one user. But it's the best solution I've experimented so far.
you can call a webmethod from javascript on unload body event.
http://www.codeproject.com/Tips/89661/Execute-server-side-code-on-close-of-a-browser
Best regards

Using HttpModules to modify the response sent to the client

I have two production websites that have similar content. One of these websites needs to be indexed by search engines and the other shouldn't. Is there a way of adding content to the response given to the client using the HttpModule?
In my case, I need the HttpModule to add to the response sent to the when the module is active on that particular web.
You'd probably want to handle the PreRequestHandlerExecute event of the application as it is run just before the IHttpHandler processes the page itself:
public class NoIndexHttpModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += AttachNoIndexMeta;
}
private void AttachNoIndexMeta(object sender, EventArgs e)
{
var page = HttpContext.Current.CurrentHandler as Page;
if (page != null && page.Header != null)
{
page.Header.Controls.Add(new LiteralControl("<meta name=\"robots\" value=\"noindex, follow\" />"));
}
}
}
The other way of doing it, is to create your own Stream implementation and apply it through Response.Filters, but that's certainly trickier.

Can I access session state from an HTTPModule?

I could really do with updating a user's session variables from within my HTTPModule, but from what I can see, it isn't possible.
UPDATE: My code is currently running inside the OnBeginRequest () event handler.
UPDATE: Following advice received so far, I tried adding this to the Init () routine in my HTTPModule:
AddHandler context.PreRequestHandlerExecute, AddressOf OnPreRequestHandlerExecute
But in my OnPreRequestHandlerExecute routine, the session state is still unavailable!
Thanks, and apologies if I'm missing something!
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regardless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
HttpContext.Current.Session should Just Work, assuming your HTTP Module isn't handling any pipeline events that occur prior to the session state being initialized...
EDIT, after clarification in comments: when handling the BeginRequest event, the Session object will indeed still be null/Nothing, as it hasn't been initialized by the ASP.NET runtime yet. To work around this, move your handling code to an event that occurs after PostAcquireRequestState -- I like PreRequestHandlerExecute for that myself, as all low-level work is pretty much done at this stage, but you still pre-empt any normal processing.
Accessing the HttpContext.Current.Session in a IHttpModule can be done in the PreRequestHandlerExecute handler.
PreRequestHandlerExecute: "Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service)." This means that before an 'aspx' page is served this event gets executed. The 'session state' is available so you can knock yourself out.
Example:
public class SessionModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += BeginTransaction;
context.EndRequest += CommitAndCloseSession;
context.PreRequestHandlerExecute += PreRequestHandlerExecute;
}
public void Dispose() { }
public void PreRequestHandlerExecute(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
context.Session["some_sesion"] = new SomeObject();
}
...
}
If you're writing a normal, basic HttpModule in a managed application that you want to apply to asp.net requests through pages or handlers, you just have to make sure you're using an event in the lifecycle after session creation. PreRequestHandlerExecute instead of Begin_Request is usually where I go. mdb has it right in his edit.
The longer code snippet originally listed as answering the question works, but is complicated and broader than the initial question. It will handle the case when the content is coming from something that doesn't have an ASP.net handler available where you can implement the IRequiresSessionState interface, thus triggering the session mechanism to make it available. (Like a static gif file on disk). It's basically setting a dummy handler that then just implements that interface to make the session available.
If you just want the session for your code, just pick the right event to handle in your module.
Since .NET 4.0 there is no need for this hack with IHttpHandler to load Session state (like one in most upvoted answer). There is a method HttpContext.SetSessionStateBehavior to define needed session behaviour.
If Session is needed on all requests set runAllManagedModulesForAllRequests to true in web.config HttpModule declaration, but be aware that there is a significant performance cost running all modules for all requests, so be sure to use preCondition="managedHandler" if you don't need Session for all requests.
For future readers here is a complete example:
web.config declaration - invoking HttpModule for all requests:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess"/>
</modules>
</system.webServer>
web.config declaration - invoking HttpModule only for managed requests:
<system.webServer>
<modules>
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess" preCondition="managedHandler"/>
</modules>
</system.webServer>
IHttpModule implementation:
namespace HttpModuleWithSessionAccess
{
public class ModuleWithSessionAccess : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += Context_BeginRequest;
context.PreRequestHandlerExecute += Context_PreRequestHandlerExecute;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if (app.Context.Session != null)
{
app.Context.Session["Random"] = $"Random value: {new Random().Next()}";
}
}
public void Dispose()
{
}
}
}
Try it: in class MyHttpModule declare:
private HttpApplication contextapp;
Then:
public void Init(HttpApplication application)
{
//Must be after AcquireRequestState - the session exist after RequestState
application.PostAcquireRequestState += new EventHandler(MyNewEvent);
this.contextapp=application;
}
And so, in another method (the event) in the same class:
public void MyNewEvent(object sender, EventArgs e)
{
//A example...
if(contextoapp.Context.Session != null)
{
this.contextapp.Context.Session.Timeout=30;
System.Diagnostics.Debug.WriteLine("Timeout changed");
}
}

Resources