When creating an ASP.NET Mvc project in Visual Studio, a Global.asax & Global.asax.cs will be created. In this .cs file you will find the standard Application_Start method.
My question is the following, how is this function called? because it is not a override. So my guess is that this method name is by convention. The same goes for the Application_Error method.
I want to know where these methods are hooked. Because I write those methods (not override them) I couldn't find any documentation on them in MSDN. (I found this page but it only tells you to hook to the Error event and shows a Application_Error(object sender, EventArgs e) but not how the Event and the method are linked.)
//Magicly called at startup
protected void Application_Start()
{
//Omitted
}
//Magicly linked with the Error event
protected void Application_Error(object sender, EventArgs e)
{
//Omitted
}
It isn't really magical.. the ASP.NET Pipeline wires all of this up.
You can see the documentation regarding this here.
Specifically you will be interested in the parts below:
An HttpApplication object is assigned to the request.
Which consists of a list of events that are fired and in what order.
There are links all over that page (too many to contain here) that link off to various other pages with even more information.
ASP.NET automatically binds application events to handlers in the
Global.asax file using the naming convention Application_event, such
as Application_BeginRequest. This is similar to the way that ASP.NET
page methods are automatically bound to events, such as the page's
Page_Load event.
Source: http://msdn.microsoft.com/en-us/library/ms178473.aspx
To demystify the 'magic' of the accepted answer, the ASP.Net pipeline is automagically binding the HttpApplication events to the methods with the Application_EventName in the class. If (much like me) you would rather see the events explicitly bound to a handler these can be bound by overriding HttpApplication.Init() and Visual Studio will generate the handler method with the correct signature.
public override void Init()
{
this.BeginRequest += MvcAppliction_BeginRequest;
}
private void MvcApplication_BeginRequest(object sender, EventArgs e)
{
...
}
There is an example of this method of binding events
ASP.Net itself creates it. Here is the flow as per MSDN -
User requests an application resource from the Web server.
ASP.NET receives the first request for the application.
ASP.NET core objects are created for each request.
An HttpApplication object is assigned to the request. In this step Global.asax will be processed and events will be associated automatically.
The request is processed by the HttpApplication pipeline. In this step the HttpApplication Global events are raised.
Here is the reference - ASP.Net Application Life Cycle.
From the reference - ASP.NET automatically binds application events to handlers in the Global.asax file using the naming convention Application_event, such as Application_BeginRequest.
Related
I'm working on a website which uses the Umbraco CMS version 7. I'm using NWebSec to implement a CSP header on the website. NWebSec has built in functionality to raise a .Net event when there's a CSP violation. Normally you'd catch that event with something like this:
protected void NWebSecHttpHeaderSecurityModule_CspViolationReported(object sender, CspViolationReportEventArgs e)
{
var report = e.ViolationReport;
var serializedReport = JsonConvert.SerializeObject(report.Details);
// Do a thing with the report
}
in the Global.asax.cs file. But so far as I can tell, Umbraco preempts the Global.asax.cs file, and it eats any event that's thrown. I have a file with a few custom event handlers like:
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
to handle the standard pieces of application startup code that would normally be in the Global.asax.cs file, but putting the NWebSec event handler in that same file doens't work. Presumably it's because it's using the .Net event handler syntax rather than whatever Umbraco replaces it with.
How do I access the events thrown by NWebSec?
the Global.asax class inherits from UmbracoApplication so no, you can't use that. There are a number of reasons for this including enabling the ability to "run" Umbraco outside of the web context - i.e. in a console application).
After reviewing the available documentation on the NWebSec documentation website, I don't think you can just place your NWebSecHttpHeaderSecurityModule_CspViolationReported event handler method in the class, you will need to wire it up as well. It should probably look something like this:
public class MyGlobalEventHandler : ApplicationEventHandler {
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
var nWebSecHttpHeaderSecurityModule = umbracoApplication.Modules["NWebSecHttpHeaderSecurityModule"] as HttpHeaderSecurityModule;
if (nWebSecHttpHeaderSecurityModule != null) {
nWebSecHttpHeaderSecurityModule.CspViolationReported += NWebSecHttpHeaderSecurityModule_CspViolationReported;
}
base.ApplicationStarted(umbracoApplication, applicationContext);
}
protected void NWebSecHttpHeaderSecurityModule_CspViolationReported(object sender, CspViolationReportEventArgs e)
{
var report = e.ViolationReport;
var serializedReport = JsonConvert.SerializeObject(report.Details);
// Do a thing with the report
}
}
If you're using a newer version of Umbraco that supports OWIN (7.3.0), you could use the NWebsec.Owin library which may give you a better result and more flexibility perhaps.
I have an IIS server (7.5) that hosts several applications each of them run in their own application pool identity. I am trying to write some code that intercepts the Session On Start event. I have successfully written other IHttpModules that are processed for all requests but, in this case I only want to intercept the first time the session is initiated. I want to do this for all the web applications at a global level within my web site. My plan is to use this to capture the last logon date for the user on a per web app basis to satisify an auditing requirement.
I have all the pieces in place except the event that I need to intercept. It seems all IHttpModule Events fire on all requests. I figured the Session_Start event would be ideal but it doesn't look like I can tie into this from an IHttpModule.
I looked at the SessionStateUtility but I do not want to rewrite session functionality, I just want to intercept the start event.
Is there another interface out there I can use to intercept Session_Start? Any other recommendations?
Have you tried something like this?
public void Init(HttpApplication context)
{
var sessionModule = context.Modules["Session"] as SessionStateModule;
if (sessionModule != null)
{
sessionModule.Start += this.Session_Start;
}
}
private void Session_Start(object sender, EventArgs e)
{
// Do whatever you want to do here.
}
I am working on an ASP.Net application and currently the Global.asax contains the usual 5 methods:
Application_Start
Application_End
Session_Start
Session_End
Application_Error
However, I needed to implement the Application_AuthenticateRequest method as well, which is not a problem, I have just added it in Global.asax but in an another application I have seen this method being implemented elsewhere in another class which implements the IHttpModule interface.
How is this possible? The same app does not have the Application_AuthenticateRequest in Global.asax, their Global.asax looks like this:
void Application_BeginRequest(object sender, EventArgs e)
{
myConfig.Init();
}
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
myConfig.Init();
if (InstallerHelper.ConnectionStringIsSet())
{
//initialize IoC
IoC.InitializeWith(new DependencyResolverFactory());
//initialize task manager
TaskManager.Instance.Initialize(NopConfig.ScheduleTasks);
TaskManager.Instance.Start();
}
}
void Application_End(object sender, EventArgs e)
{
// Code that runs on application shutdown
if (InstallerHelper.ConnectionStringIsSet())
{
TaskManager.Instance.Stop();
}
}
What makes the Application_AuthenticateRequest method run?
I would first recommend you read about HTTP handlers and modules in ASP.NET. Then you will know that in an ASP.NET application you could have multiple modules registered which will run for every request and you have the possibility to subscribe to different events of the request lifecycle, the same way you could do it in Global.asax. The advantage of this approach is that you could put the modules into a reusable assembly that you use in multiple applications and which avoids you the need to repeat the same code over and over again.
Basically the example I have been looking at created their own HTTP module and registered it in the web.config file:
They have created a new HTTP module like this:
public class MembershipHttpModule : IHttpModule
{
public void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Fires upon attempting to authenticate the user
...
}
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new EventHandler(this.Application_AuthenticateRequest);
}
}
also added the below to the web.config file:
<httpModules>
<add name="MembershipHttpModule" type="MembershipHttpModule, App_Code"/>
</httpModules>
As explained in the #Darin Dimitrov's link above: Modules must be registered to receive notifications from the request pipeline. The most common way to register an HTTP module is in the application's Web.config file. In IIS 7.0, the unified request pipeline also enables you to register a module in other ways, which includes through IIS Manager and through the Appcmd.exe command-line tool.
In Global, as an alternative to the application AutoEventWireups, it seems that events are exposed for most of the underlying Application events (BeginRequest, AuthorizeRequest, Error, etc), as well as a set of asynch methods like AddOnBeginRequestAsync etc. However, I cannot find an equivalent event for ApplicationStart!
So my question is, is there anyway to subscribe to the 'same' event that the AutoEventWireup method Application_(On)Start is hooked into?
public class Global : HttpApplication
{
public Global()
{
// I can do this ...
base.BeginRequest += new EventHandler(Global_BeginRequest);
// Or even AddOnBeginRequestAsync();
// But how can I do this?
base.ApplicationStart += new EventHandler(GlobalApplication_Start);
}
protected void Global_BeginRequest(object sender, EventArgs e)
{
// ...
}
protected void Global_ApplicationStart(object sender, EventArgs e)
{
// ...
}
}
(Out of interest ... is there a way to switch off the AutoEventWireups in Global.asax?. Using the AutoEventWireup = "false" attribute only seems to work on aspx pages)
Edit - it seems that ApplicationStart and ApplicationEnd "are special methods that do not represent HttpApplication events". So I might be barking up the wrong tree entirely.
Edit
Re : Why would I need this? Unfortunately, a corporate customer has a framework in place whereby new apps need to inherit their custom HttpApplication class, and FWR, their HttpApplication had already implemented the autowireup Application_(On)Start, meaning that I needed to find another way to override the Framework wireup, so that I can Bootstrap my IoC container and Automapper maps. As per Lloyd's answer, I could also bootstrap in the ctor or Init() as well, although this isn't quite the same. Ultimately I was able to change the corporate framework to allow multiple subscriptions.
You could override Init:
public class MyApplication : HttpApplication
{
public override void Init()
{
base.Init();
}
}
However your constructor could also work just as well.
Be really careful with the Init method.
Using Init method for code that you want to be executed once in the Application lifecycle is a bad option as the Init method is called once for every instance of the HttpApplication...
As you said ApplicationStart and ApplicationEnd are special methods that do not represent HttpApplication events but they work in a similar fashion to the Page events when the AutoEventWireups is set to true..
After jumping to the .NET source code i found that the HttpApplicationFactory class looks for a method named "Application_OnStart" or "Application_Start" in the Global.asax file and then invokes it using reflection => ReflectOnMethodInfoIfItLooksLikeEventHandler().
Check out my question about a similar subject: HttpApplication.Start event does not exist
I have a web app with loads of pages and most of them require some session variables in order to function.
i want to put some defensive code in my app. where is the best place to put somethign like:
if (Session.Count == 0){
Response.Redirect("~/default.aspx");
}
EDIT: how do i check if the current page is defult.aspx?
Quite difficult, yeah fortunately it is solved.
You need to implement Application_PreRequestHandlerExecute in Global.asax
here is the code
/// <summary>
/// The event occurs just after Initialization of Session, and before Page_Init event
/// </summary>
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
// here it checks if session is reuired, as
// .aspx requires session, and session should be available there
// .jpg, or .css doesn't require session so session will be null
// as .jpg, or .css are also http request in any case
// even if you implemented URL Rewritter, or custom IHttp Module
if (Context.Handler is IRequiresSessionState
|| Context.Handler is IReadOnlySessionState)
{
// here is your actual code
// check if session is new one
// or any of your logic
if (Session.IsNewSession
|| Session.Count < 1)
{
// for instance your login page is default.aspx
// it should not be redirected if,
// if the request is for login page (i.e. default.aspx)
if (!Context.Request.Url.AbsoluteUri.ToLower().Contains("/default.aspx"))
{
// redirect to your login page
Context.Response.Redirect("~/default.aspx");
}
}
}
}
Edit 1: Explanation & Conclusion
As one of the guys told about ASP.NET Application Life Cycle.
There are plenty of events that occurs.
Actually events in Global.asax raises in the following sequence
Validate Request // looks just internal mechanism
Perform URL Maping // looks just internal mechanism
Raise the BeginRequest event.
Raise the AuthenticateRequest event.
Raise the PostAuthenticateRequest event.
Raise the AuthorizeRequest event.
Raise the PostAuthorizeRequest event.
Raise the ResolveRequestCache event.
Raise the PostResolveRequestCache event.
Just selects a class who implemented IHttpHandler for the application // looks just internal mechanism
Raise the PostMapRequestHandler event.
Raise the AcquireRequestState event. just before raising this event asp.net loads the State like Session
Raise the PostAcquireRequestState event.
Raise the PreRequestHandlerExecute event.
Call the ProcessRequest method
Conclusion: All the events before AcquireRequestState event don't have Session object, because Session is not loaded by ASP.Net, so any event from *"AcquireRequestState** event gives Session object therefore this problem solves.
However some checks are required as I mentioned in above code
One method would be to have a Page baseclass that performs this check on Page_Init. Another method would be to piggy-back off of #K Ivanov's idea with putting it in the Global.asax. While Session is not available during Application_BeginRequest it should be available in the method Application_AcquireRequestState. For not standard web requests, this should provide access to the session to perform what you want.
in the Application_BeginRequest of the Global.asax
so to summaries the ideas we have:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if ((Session.Count == 0) &&
!(Request.Url.AbsolutePath.EndsWith("default.aspx",
StringComparison.InvariantCultureIgnoreCase)))
{
Response.Redirect("~/default.aspx");
}
}
Be careful with your approach.
I don't think it is a good idea to validate globally if certain Session information exists or not. It can get become very messy, very fast. Only certain pages might require specific Session variables, which differ from other pages. Further down the road you might even have some content which can be safely accessed without any existing Session state. Then you will have to start coding exceptions to your rule...
What type of information are you storing in these Session variables? If you elaborate further we could maybe come up with a better approach.
Be careful with Session.Count == 0, because things like Session_ID are, implicitly, stored in the session.
Preferably look for something like (Session["UserName"] == null), where Session["UserName"] is where you, explicitly, store something of the user.
Other than that, Global.asax is the best place (ASP.NET Application Life Cycle).
ALSO, you have to enter a check that you are not currently on ~/default.aspx, because otherwise you will have an infinite loop.