Source: http://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
Error pages and error logging, both so elementary and yet so complex in ASP. Perhaps complex is isn't entirely true, but it's certainly not very straight forward for a fresh ASP starter.
The reason being is there be numerous ways of implementing error pages in ASP 5 and when you search for advice yo U'll find a dozen different stackoverflow threads, each suggesting a different implementation.
Overviewwhat is the goal?
Generally when speaking of the error pages and error logging I mean error pages and logging forunhandled exceptions I n your application.
The basic goal is:
- Human Friendly error pages
- Custom page per error code (e.g.: 404, 403, etc.)
- Preserving the error code in the response to avoid Google indexing error pages
- Logging Unhandled Errors
Error pages and logging in ASP 5
I am sure one could think of many more solutions to the problem, but typically you'll find solutions which involve at Le AST one or a combination of more these methods:
- Handleerrorattribute
- Controller.onexception Method
- Application_Error Event
- customErrors element in Web. config
- httperrors element in Web. config
- Custom HttpModule
That's a lot of different ways for processing an error and they all has a historically justifyable reason. There is no golden solution which works for every application.
Before I'll go through each in detail I want to explain some fundamentals which would hopefully make the whole understand ing a lot easier.
ASP. NET MVC Fundamentals
ASP. than a HttpHandler plugged into the ASP. The easiest-illustrate-the-opening the Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
Navigating to the implementation of HttpApplication would reveal the underlying IHttpHandler and IHttpAsyncHandler INTERFAC Es:
public class HttpApplication : IComponent, IDisposable, IHttpAsyncHandler, IHttpHandler
ASP. itself is a larger the framework to process incoming requests. Even though it could handle incoming requests from different sources, it's used almost exclusively with IIS. It can be extended with httpmodules and HTTPHANDLERSG.
HttpModules is plugged into the "pipeline to process a" request at any point of the ASP. A HttpHandler is responsible for producing a response/output for a request.
IIS (Microsoft's Web server technology) would create an incoming request for ASP, which then'll start processing it a nd eventually initialize the HttpApplication (the default handler) and create a response:
The point was that ASP. NET can only handle requests which IIS have forwarded to it. This was determined by the registered httphandlers (e.g. by default A, request to a. htm file was not handled by ASP.).
And finally, MVC is only one of potentially many registered handlers within ASP.
This was crucial to understand the different solutions for error handling.
Breaking down the Optionshandleerrorattribute
The Handleerrorattribute is an MVC filterattribute, which can being applied to a class or method:
namespace System.Web.Mvc{ [AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class HandleErrorAttribute : FilterAttribute, IExceptionFilter { // ... }}
It ' s error handling capabilities is limited to action methods within the MVC framework. This means it won ' t is able to catch and process exceptions raised from outside the ASP handler (e.g. exceptions a T an earlier stage in the life cycle, errors in other handlers, etc.) Nor any exceptoins where your action method is a part of the call stack (e.g. routing errors, etc).
The Handleerrorattribute only handles internal errors. For example this won't redirect to the custom error page:
[HandleError]public ActionResult Index(){ throw new HttpException(404, "Not found");}
You can use the attribute to decorate a controller class or a particular action method. It supports custom error pages per exception type out of the box:
[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]]
In order to get the Handleerrorattribute working your also need to turn on customErrors in your web. config:
<customErrors mode="On" />
Use case
The Handleerrorattribute is the most limited in scope. Many application errors would bypass it and therefore it was not ideal for generic error handling cross application.
It is a great tool for action specific error handling though (e.g. additional fault tolerance for critical action methods) .
Controller.onexception Method
This method gets called if any action method inside the controller, throws an exception. Unlike the Handleerrorattribute it'll also catch 404 and other HTTP error codes and doesn ' t require setting customErrors Mode on.
The implementation is simple and just override the Onexception method in your controller:
protected override void OnException(ExceptionContext filterContext){ filterContext.ExceptionHandled = true; // Redirect on error: filterContext.Result = RedirectToAction("Index", "Error"); // OR set the result without redirection: filterContext.Result = new ViewResult { ViewName = "~/Views/Error/Index.cshtml" };}
You can check if the exception have alrady been handled by another component (e.g.: the Handleerrorattribute):
if (filterContext.ExceptionHandled) return;
Many solutions on the internet suggest to create a base controller class and implement the method in one location for a GE Neric error handler.
However, the fact that it's almost as limited as the Handleerrorattribute, it's not a ideal solution for generic error Handling. You'll end up duplicating your to least in one and other place.
Use case
It gives a little bit more flexibility than the handleerrorattribute, but it's still too limited for generic error proces Sing. It seems to being popular when you need to distinguish your error processing between regular and AJAX requests on a Controlle R level.
Application_Error Event
The Applicatoin_error method is far more generic than the previous the options. It is not limited to the MVC scope any longer and needs to being implemented in the Global.asax.cs:
protected void Application_Error(Object sender, EventArgs e){ var raisedException = Server.GetLastError(); // Process exception}
If you've noticed it doesn ' t come from a interface, an abstract class or an overriden method. It is purely convention based, similar like the Page_Load event in ASP Web Forms applications.
Any unhandeled exception within ASP. NET would bubble up to this event. There is also no concept for routes anymore (because it is outside the MVC scope). If you want to redirect to a specific error page that has to know the exact URL or configure it to co-exist with Customerr ORS or HttpErrors.
Use case
In terms of generic error logging The is a great place to start with! It'll capture all errors which haven ' t been handled on an earlier stage. But was careful, if you had used controller exception handling and set then the exception would not bubble up to filterContext.ExceptionHandled = true
Appl Icatoin_error.
For custom error pages It was still not perfect. This event would trigger for all ASP. Errors, but what if someone navigates to a URL which isn ' t handled by ASP. For example try navigating to http://{your-applicatoin}/a/b/c/d/e/f/g/h. The route is not mapped to ASP. NET and therefore'll not being captured in your application and provided with a custom error Page from the Application_Error event.
CustomErrors in Web. config
This Web. config setting enables provide a default error page as well as custom error pages for specific error codes :
<system.web> <customErrors mode="On" defaultRedirect="~/Error/Index"> <error statusCode="404" redirect="~/Error/NotFound"/> <error statusCode="403" redirect="~/Error/BadRequest"/> </customErrors><system.web/>
The default implementation redirects the user to the specified error page. This was really bad practise because it would change the original HTTP error code to 302 HTTP Redirect and eventually finish With HTTP $ OK at the error page. Additionally the original URL would has changed as well. This isn't only confusing but have other negative side effects too, like Google would start indexing your error pages!
Luckily you can behaviour by setting the Redirectmode to Responserewrite:
<customErrors mode="On" redirectMode="ResponseRewrite">
This fixes the initial problem, but now you'll end up with a error when redirecting to your error page:
An
exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.
It is because Responserewrite mode uses Server.Transfer under the covers, which looks for a file on your file system. As a result we need to change the redirect path to a static file, for example to an. aspx or. html file:
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx"/>
Now there are only one last issue left with this-the response code of the error page would still be OK. The only fix for the manually set of the correct error code in your. aspx error page:
<% Response.StatusCode = 404; %>
This was already pretty good in terms of the custom error pages, but we can do better! Noticed how is the customErrors section goes to the system.web section? This means we is still in the scope of ASP.
Files and routes which is not handled by our application would render a 404 page by IIS (e.g.: http://myapp/some/path/not/ Existing/image.gif).
Another downside of customErrors is so if you use a httpstatuscoderesult instead of throwing an actual exception and then I T bypass ASP. NET CustomErrors and get handled by IIS as well:
public ActionResult Index(){ return HttpNotFound(); //throw new HttpException(404, "Not found");}
There is no hacks we can apply to display a friendly error page in these cases with the customErrors technique.
Use case
The customErrors setting gets us very far, but still have its limits. You can think of it as a legacy version of HttpErrors, which have been introduced with IIS 7.0.
The only time of customErrors still make sense was if you can do it with httperrors, because you be running on IIS 6.0.
HttpErrors in Web. config
The HttpErrors section was similar to CustomErrors, but with the main difference that it's an IIS level setting rather tha n ASP/therefore needs to go into the system.webserver section in the Web. config:
<system.webServer>
It allows more configuration than CustomErrors but have its own little caveats as well. I ' ll try to explain the most important settings in a nutshell:
- HttpErrors can inherited from a higher level (e.g. set in the Machine.config)
- use the
& L T;remove/>
tag to remove the inherited setting for a specific error code.
- use the
<clear/>,
tag to remove all inherited settings.
- use the
<error/>,
tag to configure, the behaviour for one error code.
- Responsemode "Executeurl" would render a dynamic page with status code 200.
- The workaround to set the correct error code in the. aspx page works here as well.
- Responsemode "Redirect" would Redirect (302) to any URL.
- Responsemode "File" would preserve the original error code and output the static File.
- . aspx files would get output in plain text.
- . html files would render as expected.
The main advantage of HttpErrors is, it's handled on IIS level. It'll literally pick up all error codes and redirect to a friendly error page. If you want to benefit from Master Pages I recommend to go with the Executeurl and status code fix Appraoch. If you want to had rock solid error pages which IIS can serve even when hell was on earth and then I ' d recommend to go with T He static file approach (preferably. html files).
Use caseThis is currently, the best place to configure friendly error pages in one, to catch them all. The only reason does not be still running on IIS 6.0.
Custom HttpModuleLeast I wanted to quickly scratch on a custom HttpModule. It has friendly error pages anymore (HttpErrors is the The-go), but it's great for error logging.
The error event which can subscribed to insde a custom HttpModule behaves the exact same as the Application_Error E Vent. If you had both implemented then it gets called before Applicatoin_error.
The only benefit of the HttpModule are that it's reusable in and other ASP. Adding/removing a HttpModule is as simple as Adding or removing one line of code in your Web. config:
<system.webServer><modules> <add name="CustomModule" type="SampleApp.CustomModule, SampleApp"/></modules></system.webServer>
In fact, someone have already created a powerful reusable error logging module and it is open source and called ELMAH.
If you need to create application wide error logging, I highly recommend-look at this project!
Final wordsI Hope this overview is helpful to better unerstand which tool might fit your own error handling requirements in ASP. Mvc.
Each of the techniques have a justifable reason and it really depends what's need to do. Today I focused on application wide error handling and in this case I would recommend the combination of HttpErrors and an The error logging module like ELMAH to get the best out of both worlds.
Demystifying ASP. NET MVC 5 Error Pages and error Logging