Which ASP.NET life cycle events fire after HttpApplication.Error? - asp.net

I want to know which parts of the ASP.NET request life cycle happen after an error is handled via the HttpApplication.Error event. Specifically, which of the events listed at http://msdn.microsoft.com/en-us/library/bb470252.aspx#Stages fire after an error? I know that EndRequest still fires, and I suspect that PreSendRequestHeaders and PreSendRequestContent fire as well, but aside from these I have no idea.
Does it depend on when in the life cycle the error occurs? Does it depend on whether I call Server.ClearError() in the error handler?
I'm asking this question because I don't know if I should be calling HttpApplication.CompleteRequest() from my error handler.

The best way is to catch server last error and appdomain exceptions.
All of them can be done in Global.asax.cs file.
Check the following steps:
1- In Global.asax.cs, catch the last error and log it.
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Server.ClearError();
log.Error("Application Error caught in Global ", exception);
}
2- Add an event handler for UnhandledException event on AppDomain, this should be added to the Application_Start :
protected void Application_Start(object sender, EventArgs e)
{
//....
AppDomain.CurrentDomain.UnhandledException
+= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
3- And here is the implementation of CurrentDomain_UnhandledException:
void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
if (e != null)
log.Error("Domain Unhandled Exception: ", e.ExceptionObject as Exception);
}
Happy coding :)

The LogRequest event is raised even if an error occurs. You can provide an event handler for the LogRequest event to provide custom logging for the request.
for further details on http://msdn.microsoft.com/en-us/library/system.web.httpapplication.logrequest.aspx
If your application generates custom error output, suppress the default error message that is generated by ASP.NET by a call to the ClearError method in the HttpApplication.Error Event.

I didn't check, but I think it depends.
An error can be raised in any page event (Init/Load/PreRender).
After the error is raised, if you clear the error, the page lifecycle is continued from where it was.

The exception that raises the Error event can be accessed by a call to GetLastError method. If your application generates custom error output, suppress the default error message generated by ASP.NET by a call to the ClearError method.

void Application_Error(Object sender, EventArgs e)
in global.asax
http://msdn.microsoft.com/en-us/library/24395wz3%28v=vs.100%29.aspx
http://msdn.microsoft.com/en-us/library/fwzzh56s%28v=vs.100%29.aspx

Related

Using ELMAH and Application_Error at the same time

We have an ASP.NET MVC 3 application that is configured to use ELMAH. We also have code in our Application_Error method like this. Both ELMAH and our custom code log to a database.
protected void Application_Error(object sender, EventArgs e)
{
MvcApplication app = (MvcApplication)sender;
HttpContext httpContext = app.Context;
Exception ex = app.Server.GetLastError();
HttpException httpException = ex as HttpException;
//log the error with our custom logging
Server.ClearError();
if (httpContext.IsCustomErrorEnabled) //only show custom error if enabled in config
{
httpContext.Response.Clear();
httpContext.ClearError();
//show our own custom error page here
}
}
The problem (not really a problem, but whatever) we see is that both ELMAH and our custom code log the exception to the DB. I would expect calls to Server.ClearError() and httpContext.ClearError would handle the error and it would never get to ELMAH. But, does the fact that the error is being logged twice imply that ELMAH and application_error are running basically in parallel and they both receive the unhandled exception at the same time? If so is there anyway to tell ELMAH to ignore the error?
Our intent is to only have ELMAH handle an error if something goes REALLY wrong, like in the ASP.NET pipeline after elmah is registered, but before the MVC application would be running.
The issue is that it is logging to ELMAH first. So yes you can tell it not to log to ELMAH using e.Dismiss(): The ErrorLog_Filtering function below is hit before Application_Error. So add this function and any needed logic you need to determine if you want it in ELMAH or not.
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
{
//get the exceptions like:
Exception m = e.Exception;
Exception ex = e.Exception.GetBaseException();
//tell it not to log the error in ELMAH like (based on whatever criteria you have):
e.Dismiss();
//Application_Error will be hit next
}
protected void Application_Error(object sender, EventArgs e)
{
//your logic
}

NHibernate throws "Invalid operation. The connection was closed." exception

I have a code piece to record user data in Session_End event of Global.asax. It is working but sometimes it throws "Invalid operation. The connection was closed" exception. I have failed to replicate this situation on development server. It only occurs on application server. What is wrong? Thanks.
protected void Session_End(object sender, EventArgs e)
{
try
{
userlog = UserLog.LoadBySessionAndLogoutTime(NHibernateHTTPModule.CurrentSession, Session.SessionID, null);
userlog.LogoutTime = DateTime.Now;
UserLog.Update(NHibernateHTTPModule.CurrentSession, userlog);
}
catch (Exception exception)
{
Mail.SendMail("Error", error);
}
}
It would help if you explain where you open and close your nhibernate session. Unless you say the opposite I'll assume that you use "Open Session in View" pattern.
The user session ends outside of the scope of a request so if you open a nhibernate session on each request it won't be available when you try to log.
You should verify if you have an opened nhibernate session before logging. If none is active, you'll need to open a new one.
Chances are an Exception relating to an NHibernate operation was thrown somewhere in your code. Any NHibernate exception will invalidate the session.
So there is some condition on the application server that is different from your dev environment that contributes to the error. You could try logging it on the application server:
protected void Application_Error()
{
Exception myError = null;
if(HttpContext.Current.Server.GetLastError() != null)
{
// log error
myError = Server.GetLastError();
....
}
}
in global.asax will intercept all errors so you can log them.

How do I catch an exception raised on my ASCX control (not the code-behind)?

I have a large ASPX page with many ASCX controls. If a control throws an exception, it should log the exception and hide only itself. All the other controls should still render.
How do I handle exceptions on individual ASCX's raised from the front-end file (the ASCX and not the code-behind)? for example: a control trying to reference an invalid property using the <%= MethodThatThrowsANullReferenceException() %> syntax.
Obviously using the generic error handler method in Global.asax won't solve the problem. I need to handle exceptions on individual controls.
Make all your UserControls inherit from a custom base class, like such:
public class CustomUserControl : UserControl
{
protected override void Render(HtmlTextWriter writer)
{
try
{
base.Render(writer);
}
catch (Exception e)
{
writer.Write("Could not load control. Sad face.");
}
}
}
I tried overriding Render method but this doesn't cover all exceptions.
For example, if some kind of exception is thrown during Page_Init, Load or Render, this will prevent the page from rendering.
We have different people working on different modules (controls) that can be loaded into a single Page, but I'm not responsible for the quality of the code of each module, so even if it's not best practice, i needed to catch exceptions and identify which control fails to load, because the application can't fail just because one module does.
For this particular scenario that is not so rare nowadays, neither custom, application or page error handling will work well.
The solution I've come up was:
Each Module (Control.ascx) when needs to be loaded into the Page (aspx) , is contained into a ModuleShell that will hold some specific features and will be responsible for helping the Page_Error handling to work properly.
This ModuleShell , instead of trying to trap the exception of its child control that failed, will just monitor in each life cycle stage if it managed to Load properly.
Here's an snippet of it:
protected void Page_Init(object sender, EventArgs e)
{
Modules.CurrentState = _mod;
}
protected void Page_Load(object sender, EventArgs e)
{
Modules.CurrentState = _mod;
}
protected void Page_PreRender(object sender, EventArgs e)
{
Modules.CurrentState = _mod;
}
Modules is a static class used to store session variables.
CurrentState is a variable that ModuleShell use to record their names in.
The Page_Error located in the only aspx we got, will get the last recorded ModuleShell that tried to load. Since any exception will stop page rendering, the last ModuleShell to record its name to the main Page, it's probably the one that failed to load properly.
It's a sloppy solution but it's transparent to the Module Developer.
AFAIK, this is not possible (at least in an easy way).
Rich Custom Error Handling with ASP.NET:
When errors happen, an exception is
raised or thrown. There are three
layers at which you may trap and deal
with an exception: in a
try...catch...finally block, at the
Page level, or at the Application
level. The first two happen right
inside a page's code, and code for
application events is kept inside
global.asax.
The Exception object contains
information about the error, and as
the event bubbles up through the
layers, it is wrapped in further
detail. In rough terms, the
Application_Error exception contains
the Page_Error exception, which
expands on the base Exception, which
triggered the bubbling in the first
place.
If there is an exception occured inside the user control, the only way to catch it inside the user control is to handle it inside a try { } catch { } block.
I think the lowest level when the exception like this could be caught is the next - Page_Error level like this:
protected void Page_Error(object sender, EventArgs e)
{
// the control which throw an exception
var control = (Control)sender;
control.Visible = false;
// the exception itself
var exception = Server.GetLastError();
Context.ClearError();
}
the Context.ClearError() method is even preventing an exception from bubbling up further on to Application_Error. But unfortunatelly, then unhandled exception is thrown the page processing stops and error processing is started instead. This means the render of page will stop too (so you won't see the controls next to that which caused this exception).
You could wrap the method you are trying to call in your own method that will return the same type, but with a try {} catch {} block.
public string MethodWrapper()
{
try
{
return MethodThatCanThrowException();
}
catch (SomeExceptionType)
{
//log exception
return string;
}
}
One option, as suggested by Jim Bolla was to make all controls inherit from the same base class and use a Try/Catch in the Render method. This would have worked. Unfortunately many of the controls I am dealing with already have different base classes.
This solution worked for me:
I added the following code to each user control (I'm sure this can be refactored further to reduce duplication):
#region Error Handling
public event EventHandler ControlCrashed;
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void RenderChildren(HtmlTextWriter writer)
{
try
{
base.RenderChildren(writer);
}
catch (Exception exc)
{
Logger.Error("Control failed to load. Hiding control. Message: " + exc, exc);
//Ignore and hide the control.
this.Visible = false;
if (ControlCrashed != null)
ControlCrashed(this, EventArgs.Empty);
}
}
#endregion
This catches any front-end rendering problems. The parent page can handle the ControlCrashed event if it wishes to display a nice error message.

3rd party Slimee DatePicker: how to handle parse error?

I need to use the mentioned 3rd party datepicker and it throws an exception when an invalid date it entered. The author only exposes one event, which is fired when a successful parse takes place. How, in ASP.NET could I catch this error and do something about it, like set a label's text?
There are a couple of approaches you can take here, personally I would replace the default event handler for the TextChanged event via inheritance.
The code assigns one via during the setup and unfortunately textbox is a private member
textBox.TextChanged += new EventHandler(OnSelectedDateChanged);
which is declared as
protected virtual void OnSelectedDateChanged(object sender, EventArgs e)
So we can inherit SlimeeLibrary.DatePicker
public class EnhancedDatePicker : SlimeeLibrary.DatePicker
and then override the EventHandler raising a new parse error event.
public event EventHandler OnDateParseError;
protected override void OnSelectedDateChanged(object sender, EventArgs e)
{
try
{
base.OnSelectedDateChanged(sender, e);
}
catch (FormatException fe)
{
OnDateParseError(sender, e);
}
}
Hope that helps. I haven't checked it but have examined the code for slimees control, but don't want to setup a code project account to download it sorry. You'll obviously need to change your ASP.NET usercontrol references to use the new class.

How do I override page load lifecycle in ASP.NET to prevent ASPX form from loading?

I need to test a condition in several ASPX code-behind files and, in some cases, would like to completely bypass the normal page load process so that the corresponding ASPX page is not loaded. Intead, I'd like to send a custom response to the browser that's written from a code-behind method.
Does anyone know where to start- what method(s) in the page lifecycle to override and the best technique to ensure that my custom Response.Write is sent to the browser while the normal ASPX page content is suppressed?
Thanks.
Probably the easiest way to do it - use Page_Load().
protected void Page_Load(object sender, EventArgs e)
{
bool customResponse = true;
if (customResponse)
{
Response.Write("I am sending a custom response");
Response.End(); //this is what keeps it from continuing on...
}
}
The "easy" way to do it, with Response.End() is terrible for performance, throwing an exception which terminates the thread.
http://blogs.msdn.com/b/tmarq/archive/2009/06/25/correct-use-of-system-web-httpresponse-redirect.aspx
http://weblogs.asp.net/hajan/archive/2010/09/26/why-not-to-use-httpresponse-close-and-httpresponse-end.aspx
I had the same question and solved it this way. It's a two-step process: First call HttpApplication.CompleteRequest() and exit your processing. Next override Render() so that the base method is not called. The example code then becomes:
bool customResponse = true;
protected void Page_Load(object sender, EventArgs e)
{
if (customResponse)
{
Response.Write("I am sending a custom response");
this.Context.ApplicationInstance.CompleteRequest();
return; // Bypass normal processing.
}
// Normal processing...
}
protected override void Render(HtmlTextWriter writer)
{
if (!customResponse)
base.Render(writer); // Then write the page as usual.
}
It really depends what you're responding to, is it a posted Form field, authentication info etc...?
The method shown using Page_Load will work, but anything before that point in the page lifecycle will also execute.

Resources