How to handle exceptions during an ASP.NET request lifecycle - asp.net

This question is kind of related to Handle URI hacking gracefully in ASP.NET in that it's too about how to best handle exceptions that occur during an ASP.NET request lifecycle. I've found a way to handle most exceptions gracefully, but then I've found that some exceptions occur so late in the request that there's no way to do stuff like Server.Transfer to compartementalize the whole error presentation logic into its own page.
So, I have to handle the exception inside the Application_Error event instead, and do Response.Writes and whatnot. It's ugly. I understand that in some circumstances the response stream could have already been flushed, so transferring the request isn't really an option. What I want to ask is if there's anyone who's found an elegant solution to this problem?
Also, I find it difficult to know when I can handle the exception gracefully by transferring the request to another page and not. What is the best way to find out where in the request lifecycle we're at when an exception occurs? If it occurs during the loading and rendering of a page, Page_Error will be able to handle it and I've not had a problem doing Server.Transfer there yet. But if the exception occurs either too early or too late for Page_Error to catch it and it bubbles to Application_Error, what do I do to know whether it's early or late?
If it's late in the lifecycle, I'll probably have to do Response.Write directly from Application_Error, but if it's early I can do Server.Transfer. The problem is that trying to do Server.Transfer will itself cause an exception if it's too in the request to do it.
So, is there a global enumeration or something similar that will indicate whether it's too late to do creative stuff with the response or not?

I have used this approach to catch all errors generated, either in web controls or pages. All it requires you to do is to inherit from a base class (one for pages and one for usercontrols) each page or usercontrol can implement its own HandleException method and do whatever it needs to do.
Full code here:
Transparent generic exception handling for asp.net / MOSS2007 (with code)

I think my advice for this would be to use ASP.NET Health Monitoring with WMI Events provider for the errors:
Here is a how to.
http://msdn.microsoft.com/en-us/library/ms178713.aspx
Hope this helps:
Andrew

I suggest that you use asp.net configuration to have a general error page for the unhandled exceptions. From the sample web.config
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
On the overall handler, just log the exception, and let asp.net do the redirect.
If you still want to go on with your customer approach, I suggest you look at the available asp.net source and check how it is doing that.

Related

WebMethod not returning Exception data

Doing an $.Ajax() POST call to a WebMethod in a WebForms WebSite application. The calls work fine, except for when exceptions occur in the WebMethod. They are not passed down. From the WebMethod, the $.Ajax call returns an xdr.responseJson object with ExceptionType, Message, and StackTrace attributes. This is where I would expect to find the exception info but instead, no matter what the exception is on the server, ExceptionType returns "", Message is always "There was an error processing the request." and the StackTrace is "". What needs to be done for these to be populated with meaningful data from the server?
I ended up solving this by changing the web.config:
<customErrors mode="Off" />
After that change, Message, ExceptionType, and Stack trace became properly populated instead of the standard "There was an error processing the request." which gets returned for all exceptions when mode=on and is not very helpful for debugging or reporting.
Wish this setting was more granular so I could turn it off only for WebMethods.

Events in Global.asax

Below sample url is given
localhost/MyPage.aspx/1582%20
asp.net throw an error saying that this page does not exist and as far as I know it is because of the %20 at the end of the URL.
the error is being caught by Application_Error in Global.asax, however I would like this to be redirected to a page and I would also like my Application_Error to log the errors, no page redirection
Are there other events in Global.asax that I can use to handle this redirection? I tried PreSendRequestHeaders but still it goes to Application_Error
Thanks.
No, you can't as there is only one event for such situation and it is Application_Error (event handlers in Global.asax are for HttpApplication).
But you can implement an IHttpModule, parse the url in it, and if it is correct, redirect your user to target page (see also here).
You should remember that, by default, not all the requests are sent to the such modules, so you might have register your module in web.config by various way:
Classic Mode
<configuration>
<system.web>
<httpModules><add name="HelloWorldModule" type="HelloWorldModule"/></httpModules>
</system.web>
</configuration>
Integrated Mode
<configuration>
<system.webServer><modules><add name="HelloWorldModule" type="HelloWorldModule"/></modules></system.webServer>
</configuration>
HTTP Handlers and HTTP Modules Overview

Intermittent ASP.net IIS8.5 uncatchable 500 internal-server-error on Azure cloud service

Lets start with a little background information. I am running a very simple ASP.net MVC Azure cloud service (a web role, Windows Server 2012 R2 with IIS 8.5). This service receives statistics from a flash client, which posts data roughly every 10 seconds (for potentially very large number of clients) and JavaScript. All the service contains is a single controller with two simple actions with a bunch of parameters (representing the individual statistics which are send in various combinations). All the service does is set the CORS and cookie responses (the clients/JavaScript can be embedded on random domains), verify the integrity of the received data and then store it into an Azure table storage account.
In order to ensure our service operates optimally we use New Relic to track service performance, and in order to ensure that our data is accurate (i.e. we successfully record all received messages) we implemented a custom error handling solution so we can fix any problems/bugs that might arise.
We have load tested our service using jmeter and encountered no problems, but now that we have deployed to a live environment and our service is being used we are starting to encounter occasional 500 internal server errors (approx 5% of requests). The big problem being that our own error handling code is not detecting these errors, however New Relic does report certain requests generating a 500 internal server error (with no further information like a stack trace, sometimes with, sometimes without reported parameters).
Our custom error handling consists of an HTTP module which registers to both the AppDomain.CurrentDomain.UnhandledException and the context.Error events. In theory this should be catching (and then logging) any exceptions which are not already being caught (and logged) inside our own code. Relevant web.config sections are configured in the following manner:
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/500.aspx">
<error statusCode="404" redirect="~/404.aspx" />
<error statusCode="500" redirect="~/500.aspx" />
</customErrors>
and
<httpErrors existingResponse="Replace">
<clear />
<error statusCode="404" path="404.html" responseMode="File" />
<error statusCode="500" path="500.html" responseMode="File" />
</httpErrors>
<modules>
<add type="namespace.UnhandledExceptionModule" name="UnhandledExceptionModule" preCondition="managedHandler" />
</modules>
However, this is not the case. I have tried turning on all kinds of logging but the IIS logs are useless (they only show that a 500 response was returned, but no other useful information). The only useful information I have been able to gather is from the failed request traces, but I have not been able to determine what the actual problem is from that information (googling the error code or exception leads to nothing concrete). A screenshot of the relevant section of a failed trace can be found here:
http://i57.tinypic.com/20acrip.jpg
I also uploaded the complete trace here:
http://pastebin.com/fDt3thvr
Each failed request generates exactly the same log, so the errors we are seeing are consistently being caused by the same problem. However, I am not able to determine what this problem is, let alone find a way of fixing it. Even though I have an error code and message, googling them only returns very old topics on issues that have been fixed 6 years ago.
It is pretty important for our business that these messages can be recorded with a high degree of accuracy, but as it stands now I have no further ideas on how to gain better information on what is happening on these servers. We are also not able to replicate this behavior in a controlled environment.
Also, our error logging itself does work properly. 'Normal' errors are logged as expected and we have also verified the HTTP module actually works.
Edit:
The controller pseudo code is as follows:
[HttpPost]
public ActionResult Method(...)
{
// Set cookie and CORS reponse, check for early out.
if(earlyOut)
return 404;
// Store received values.
azuretable.ExecuteAsync(TableOperation.InsertOrMerge(...));
return 200;
}
Edit2:
I have spend some time analyzing failed request traces and they mostly seem to be generated by users with IE9. I actually managed to reproduce the error 2 times by quickly leaving the page while it is loading, as the problem seems to be caused by aborted Ajax calls (which we make the most of during page load). Why would an aborted call cause a 500 error though instead of being handled neatly?
Do the cookies exceed 4k ? The same thing happened to us on IIS, and the requests sometimes ended up with 500 Internal Server error. The errors were virtually untraceable.
I reproduced the issue by simply inflating a cookie over the 4093 bytes limit.
I think that it is because you are not awaiting your async method call, or your are not returning an awaitable response. I had exactly this issue when I forgot to do that.
await azuretable.ExecuteAsync(TableOperation.InsertOrMerge(...))
Then you should be good. I think you'll find that the async call is finishing after your call has completed back to the caller.

ASP.NET Common Error Page Best Practises

I am working on an ASP.NET web application. I am implementing the logging framework for the entire application.
web application has around 7-8 pages and is a simple CRUD operations web application.
Its an Azure hosted application. Following is the approach i am following for logging and exception handling.
1) Added Try...Catch blocks in the Data Access Layer, and Click events.
2) Upon catching errors, I am propagating the exceptions upto the Globabl.asax leve, and in Application_Error event logging the error into Event Logs and Trace Logs.
3) After this in the Global.asax file I am Redirecting to an Error Page to show a User Friendly Message and link to the failed page.
4) Just wanted to know whether is it a good approach to do this.
Thanks Friends.
Are you actually handling exceptions on the DAL (ie, logging, trying to fix it, etc)? If not, then the try catch serves no purpose other than spinning cycles. The same is true for the click events, but it is not a bad practice to handle errors on the UI, even if you are not truly doing anything with them, as you will divert the user from the ugly error page to your own friendly message.
A single error page works fine, if you truly cannot handle the exception thrown. The upside is time to market, as you write precious little code to avoid showing the user an ugly message. The downside is the user misses context. I am not really up on the one size fits all exception handler, except as a backup (have an error I did not envision that got past my first line handler).
There is a variation of the common error page, if you are handling based on HTTP statuses and that is to use config.
Another pattern is to set up your own base page and have it work as an error handler. You can then set aside a container to fill when an error occurs. This approach works nicely for adding context, as the user still sees part of the page he was on, but you have given an error message, so he knows things have failed. I have seent this pattern used with a user control that is added to the container when an error occurs, but this is a bit more invovled, as you have to set up a table of codes and proper messages to show (which can be buggy in and of itself).
Why not using ASP.NET custom error pages ? You can specify each error page for each status code or you can specify a default redirect.
You can configure this in the web config and you are all set.
<customErrors defaultRedirect="GenericError.htm" mode="On">
<error statusCode="404" redirect="notfound.htm"/>
</customErrors>
You can configure it for showing the custom error pages to all users or only to remote users etc..
http://aspnetresources.com/articles/CustomErrorPages
I totally agree that you should log all errors in your catch block and write it to a log.
It sounds like you're kind of reinventing the wheel here. ASP.NET already includes things to help you achieve the desired result. Unless you need handling logic to cleanup after the errors, I wouldn't use try catch blocks. Have a look at the ASP.NET Health Monitoring Overview for logging errors. As far as presenting a custom error page see How to create custom error reporting pages in ASP.NET by using Visual C# .NET.
I think, You need not to use Exception Handling. Suppose You have a Presentation Layer and a Business Logic Layer/DataAccess Layer.
Upon facing the error in say Business Logic, it will move directly to Glogal.asax.cs file under Application_Error Event without going back to the calling function. Here you can log the error message like below....
HttpContext.Current.Server.GetLastError().InnerException.StackTrace
HttpContext.Current.Server.GetLastError().InnerException.Message
HttpContext.Current.Server.GetLastError().InnerException.Source
HttpContext.Current.Server.GetLastError().InnerException.TargetSite.DeclaringType.FullName
HttpContext.Current.Server.GetLastError().InnerException.TargetSite.DeclaringType.Name
HttpContext.Current.Server.GetLastError().InnerException.TargetSite.DeclaringType.Namespace
Now in the Web Config you can write code to redirect the user on some default page like below.
<customErrors defaultRedirect="ErrorPage.htm" mode="On">
<error statusCode="404" redirect="ErrorPageNotFound.htm"/>
</customErrors>

IIS7 Overrides customErrors when setting Response.StatusCode?

Having a weird problem here. Everybody knows that if you use web.config's customErrors section to make a custom error page, that you should set your Response.StatusCode to whatever is appropriate. For example, if I make a custom 404 page and name it 404.aspx, I could put <% Response.StatusCode = 404 %> in the contents in order to make it have a true 404 status header.
Follow me so far? Good. Now try to do this on IIS7. I cannot get it to work, period. If Response.StatusCode is set in the custom error page, IIS7 seems to override the custom error page completely, and shows its own status page (if you have one configured.)
Has anyone else seen this behavior and also maybe know how to work around it? It was working under IIS6, so I don't know why things changed.
Note: This is not the same as the issue in ASP.NET Custom 404 Returning 200 OK Instead of 404 Not Found
Set existingResponse to PassThrough in system.webServer/httpErrors section:
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
Default value of existingResponse property is Auto:
Auto tells custom error module to do the right thing. Actual error text seen by clients will be affected depending on value of fTrySkipCustomErrors returned in IHttpResponse::GetStatus call. When fTrySkipCustomErrors is set to true, custom error module will let the response pass through but if it is set to false, custom errors module replaces text with its own text.
More information: What to expect from IIS7 custom error module
The easiest way to make the behavior consistent is to clear the error and use Response.TrySkipIisCustomErrors and set it to true. This will override the IIS global error page handling from within your page or the global error handler in Application_Error.
Server.ClearError();
Response.TrySkipIisCustomErrors = true;
Typically you should do this in your Application_Error handler that handles all errors that your application error handlers are not catching.
More detailed info can be found in this blog post:
http://www.west-wind.com/weblog/posts/745738.aspx
Solved: It turns out that "Detailed Errors" needs to be on in order for IIS7 to "passthrough" any error page you might have. See http://forums.iis.net/t/1146653.aspx
I'm not sure if this is similar in nature or not, but I solved an issue that sounds similar on the surface and here's how I handled it.
First of all, the default value for existingResponse (Auto) was the correct answer in my case, since I have a custom 404, 400 and 500 (I could create others, but these three will suffice for what I'm doing). Here are the relevant sections that helped me.
From web.config:
<customErrors mode="Off" />
And
<httpErrors errorMode="Custom" existingResponse="Auto" defaultResponseMode="ExecuteURL">
<clear />
<error statusCode="404" path="/errors/404.aspx" responseMode="ExecuteURL" />
<error statusCode="500" path="/errors/500.aspx" responseMode="ExecuteURL" />
<error statusCode="400" path="/errors/400.aspx" responseMode="ExecuteURL" />
</httpErrors>
From there, I added this into Application_Error on global.asax:
Response.TrySkipIisCustomErrors = True
On each of my custom error pages I had to include the correct response status code. In my case, I'm using a custom 404 to send users to different sections of my site, so I don't want a 404 status code returned unless it actually is a dead page.
Anyway, that's how I did it. Hope that helps someone.
This issue has been a major headache. None of the suggestions previously mentioned alone solved it for me, so I'm including my solution. For the record, our environment/platform uses:
.NET Framework 4
MVC 3
IIS8 (workstation) and IIS7 (web server)
Specifically, I was trying to get an HTTP 404 response that would redirect the user to our custom 404 page (via the Web.config settings).
First, my code had to throw an HttpException. Returning a NotFoundResult from the controller did not achieve the results I was after.
throw new HttpException(404, "There is no class with that subject");
Then I had to configure both the customErrors and httpError nodes in the Web.config.
<customErrors mode="On" defaultRedirect="/classes/Error.aspx">
<error statusCode="404" redirect="/classes/404.html" />
</customErrors>
...
<httpErrors errorMode="Custom" existingResponse="Auto" defaultResponseMode="ExecuteURL">
<clear />
<error statusCode="404" path="/classes/404.aspx" responseMode="ExecuteURL" />
</httpErrors>
Note that I left the existingResponse as Auto, which is different than the solution #sefl provided.
The customErrors settings appeared to be necessary for handling my explicitly thrown HttpException, while the httpErrors node handled URLs that fell outside of the route patterns specified in Globals.asax.cs.
P.S. With these settings I did not need to set Response.TrySkipIisCustomErrors
TrySkipIisCustomErrors is only a part of a puzzle. If you use Custom Error Pages but you also want to deliver some RESTful content based on 4xx statuses then you have a problem. Setting web.config's httpErrors.existingResponse to "Auto" does not work, because .net seems to always deliver some page content to IIS, therefore using "Auto" causes all (or at least some) Custom Error Pages to be not used. Using "Replace" won't work too, because response will contain your http status code, but its content will be empty or filled with Custom Error Page. And the "PassThrough" in fact turns the CEP off, so it can't be used.
So if you want to bypass CEP for some cases (by bypassing I mean returning status 4xx with some content) you will need additional step: clean the error:
void Application_Error(object sender, EventArgs e)
{
var httpException = Context.Server.GetLastError() as HttpException;
var statusCode = httpException != null ? httpException.GetHttpCode() : (int)HttpStatusCode.InternalServerError;
Context.Server.ClearError();
Context.Response.StatusCode = statusCode;
}
So if you want to use REST response (i.e. 400 - Bad Request) and send some content with it, you will just need to set TrySkipIisCustomErrors somewhere in action and set existingResponse to "Auto" in httpErrors section in web.config. Now:
when there's no error (action returns 4xx or 5xx) and some content is returned the CEP is not used and the content is passed to client;
when there's an error (an exception is thrown) the content returned by error handlers is removed, so the CEP is used.
If you want to return status with empty content from you action it will be treated as an empty response and CEP will be shown, so there's some room to improve this code.
By default IIS 7 uses detailed custom error messages so I would assume that Response.StatusCode will equal 404.XX rather than just 404.
You can configure IIS7 to use the simpler error message codes or modify your code handling the more detailed error messages that IIS7 offers.
More info available here:
http://blogs.iis.net/rakkimk/archive/2008/10/03/iis7-enabling-custom-error-pages.aspx
Further investigation revealed I had it the wrong way around - detailed messages aren't by default but perhaps they've been turned on, on your box if you're seeing the different error messages that you've mentioned.

Resources