ASP.NET Request Lifecycle - asp.net

say I have a folder in my web app, with an index.html inside it:
/test
/test/index.html
and the following Module
public class TestModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.ReleaseRequestState += context_ReleaseRequestState;
}
static void context_ReleaseRequestState(object sender, EventArgs e)
{
var application = sender as HttpApplication;
// foo
}
}
If I make a request to /test/index.html, context_ReleaseRequestState fires and the response is returned, great.
If I make a request to /test/, context_ReleaseRequestState fires as above, the response is returned, but then context_ReleaseRequestState fires again!
The response has already been returned (and the connection closed, etc).
The HttpContext.Response object has the followng properties set on it:
HeadersWritten: true
Headers: Server=Microsoft-IIS%2f7.5
What is causing this second invocation?
on the Requestthe FilePath property for the first invocation is /test/index.html, and for the second its /test/
Is there some property on the HttpApplication or HttpRequest that I can check that identifies this 2nd request for whatever it is? The HeadersWritten property is internal :(
Thanks

Related

Redirect URL Using HttpModule Asp.net

I have created an HttpModule so that Whenever I type "localhost/blabla.html" in the browser, it will redirect me to www.google.com (this is just an example, it's really to redirect requests coming from mobile phones)
My Questions are :
1) How do I tell IIS(7.0) to redirect each request to the "HttpModule" so that it is independent of the website. I can change the web.config but that's it.
2) Do I need to add the .dll to the GAC? If so, How can I do that?
3) The HttpModule code uses 'log4net' . do I need to add 'log4net' to the GAC as well?
Thanks
P.S. the site is using .net 2.0.
You can use request object in BeginRequest event
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(this.context_BeginRequest);
}
private void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
//check here context.Request for using request object
if(context.Request.FilePath.Contains("blahblah.html"))
{
context.Response.Redirect("http://www.google.com");
}
}
}

Logging raw and compressed HTTP responses in ASP.NET & IIS7

Along the lines of this question I want to create a HttpModule that will do some custom logging of requests and responses for us. Using the code in the most popular answer for that question I've got a HttpModule up and running which does indeed work:
class PortalTrafficModule : IHttpModule
{
public void Dispose()
{
// Do Nothing
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.EndRequest += new EventHandler(context_EndRequest);
}
private void context_BeginRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
// Create and attach the OutputFilterStream to the Response and store it for later retrieval.
OutputFilterStream filter = new OutputFilterStream(context.Response.Filter);
context.Response.Filter = filter;
context.Items.Add("OutputFilter", filter);
// TODO: If required the request headers and content could be recorded here
}
private void context_EndRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
OutputFilterStream filter = context.Items["OutputFilter"] as OutputFilterStream;
if (filter != null)
{
// TODO: Log here - for now just debug.
Debug.WriteLine("{0},{1},{2}",
context.Response.Status,
context.Request.Path,
filter.ReadStream().Length);
}
}
}
(note that the OutputFilterStream class referred to in the code is in the referenced question).
However, the responses seem to be missing some HTTP headers that I see in Fiddler (like "Date") and more importantly, when I turn on compression the responses I'm logging aren't compressed whereas what I see in Fiddler is.
So my question - is it possible to log the compressed content or is this happening in a subsequent step my module can't hook in to?
For the record, I've also tried handling the PreSendRequestContent event and the response is still uncompressed.
Hi although I cannot answer your questions directly I have had cause to do similar things in the past and have found the following resource extremely helpful and enlightening. In the end I managed to acheive what I needed for raw soap headers by configuring the System.Diagnostics node within web config and create a trace log of traffic. I understand that your needs may be more granular than that however I believe this resource may still help.
http://msdn.microsoft.com/en-us/library/ms731859
Of particular interest may be the message log configuration and viewing message logs links from the one above.

IHttpModule is not being called for my WebMethod

Ok, so I have an existing application to which I have added a custom HttpModule. I'm registering two events in the Init() method (PreRequestHandlerExecute and PostRequestHandlerExecute). The HttpModule gets called for every 'normal' request. But not I have created an .aspx containing a few WebMethods that are being called for ajaxifying some UI components. The WebMethod gets called nicely, but the trouble is that my HttpModule does NOT get called at all (no events, no init, even no constructor) when accessing the WebMethod. The module gets called nicely when accessing the .aspx in question as a 'normal' request. But it refuses to be called when calling the WebMethod.
My .aspx looks like this:
public partial class SelectionListService : System.Web.UI.Page
{
[WebMethod]
[ScriptMethod]
public static RadComboBoxData GetItemsAsRadComboBoxData(RadComboBoxContext context)
{
...
}
}
My HttpModule look like this:
public class MyModule : IHttpModule, IRequiresSessionState
{
public MyModule ()
{
}
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute);
context.PostRequestHandlerExecute += new EventHandler(Application_PostRequestHandlerExecute);
}
private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
...
}
private void Application_PostRequestHandlerExecute(object sender, EventArgs e)
{
...
}
}
I have been digging into this for quite some time now, but I just can't get it to work. Any ideas?
PS1: the BeginRequest, etc in global.asax.cs do get called when accessing the WebMethod.
PS2: I'm running IIS7 on Windows7.
since PageMethods must be static, an instance of the Page class with all it's events and the ASP.NET pipeline never happens. You simply get the result of your PageMethod call, and that is all.
I have a project that had the same problem. We found that the first event in the pipeline that we could get to fire for the WebMethods was the AcquireRequestState event. We hooked into that with our HttpModule in order to do the authorization checking required for the application.
I don't know what your pre and post request handlers do, but maybe you could shift some of the logic into the AcquireRequestState event handler.

HttpModule is breaking PostBack events

I'm trying to setup a simple HttpModule to handle authentication between my single sign on server. I've included code for the module below. The module is hitting my SSO and properly authenticating; however, on pages with forms the postback events are not occurring properly (e.g. isPostBack value is always false even though a POST occurred, button click events don't get hit, etc.).
public sealed class MyAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnAuthenticateRequest;
}
public void Dispose()
{
}
public static void OnAuthenticateRequest(object sender, EventArgs e)
{
FormsAuthentication.Initialize();
HttpContext context = HttpContext.Current;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
// Validate the ticket coming back from the authentication server
if (!string.IsNullOrEmpty(request["ticket"]))
{
// I can include code for this if you want, but it appears to be
// working correct as whenever I get a ticket from my SSO it is processed
// correctly. I only get a ticket after coming from the SSO server and
// then it is removed from the URL so this only gets hit once.
MyAuthentication.ProcessTicketValidation();
}
if (!request.IsAuthenticated)
{
// redirect to the login server
response.Redirect("https://sso.example.com/login.aspx" + "?" + "service=" +
HttpUtility.UrlEncode(context.Request.Url.AbsoluteUri), false);
}
}
}
EDIT
I would also like to note that if I change the line:
if (!string.IsNullOrEmpty(request["ticket"]))
to:
if (!string.IsNullOrEmpty(request.QueryString["ticket"]))
the problem goes away.
Is it possible that your postbacks have a duplicate form data variable, "ticket"? That would seem to explain the behavior to me.
Aside from that, this line is suspicous:
FormsAuthentication.Initialize();
The FormsAuthentication class uses the "Provider" pattern, which means it's a singleton. You should not re-initialize. From the msdn documentation:
The Initialize method is called when the FormsAuthenticationModule
creates an instance of the FormsAuthentication class. This method is
not intended to be called from your code.

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