Custom error message in ASP.NET MVC when an Exception has been thrown in HttpModule
I am using ASP.NET MVC. I trap any unhandled exception on OnException
in BaseController
to properly show the nice message.
However I have an Http Module project in the same solution. The module may throw an InvalidOperationException
if user make a wrong request. I am using IIS7 Integrated mode and all has been configured properly. I am new to IIS life time and Http module exception.
Googling only found the way to do exception handling using HttpMethod which is different from this case.
The question is if the exception has been thrown in the module does the MVC will execute or not? If it does why it not going to OnException
? and how to get the exception in the MVC like at Action method?
Edit: Based on answer @nickvane about application lifecycle, I try to explain the situation better. What I think is any HttpModule
even created by me may throwing any exception. I want this exception being handled by my MVC which is in UrlRoutingModule
. I am thinking of a:
- The order of HttpModule
- Unhandled ex开发者_如何转开发ception does stop the module and next module will never run?
- How to handle an exception in MVC web app (
UrlRoutingModule
) if just thrown in another HttpModule either executed before or afterUrlRoutingModule
. - Can I get the exception thrown in another module by using
context.AllErrors
orcontext.Error
? But I unable to get the errror.
The only way I found on the planet is change from the throwing an exception to Server.Transfer()
but how about the other modules that is not mine, it still throwing an exception and I want to catch it.
Think of this: I create MVC Restful API which MUST return a JSON. I can properly handled any exception in MVC app using OnException
so that any unhandled exception will return somethig like {"Error": "An error message here."}. Now I have HttpModule that posibbly throw an error. The module does not concern about the API and JSON, it simply throw an exception. Then I try to catch this exception in MVC to return in the JSON formatted.
The lifetime of a controller or a httpmodule are very different. This link shows the lifecycle of an asp.net application:
http://msdn.microsoft.com/en-us/library/bb470252.aspx
Although it isn't for asp.net mvc specifically, the mvc framework plugs into this lifecycle and follows this pipeline:
http://ajaxus.net/wp-content/uploads/2010/01/asp_net_mvc_poster.pdf
From http://www.asp.net/mvc/tutorials/understanding-the-asp-net-mvc-execution-process-vb
Requests to an ASP.NET MVC-based Web application first pass through the UrlRoutingModule object, which is an HTTP module. This module parses the request and performs route selection. The UrlRoutingModule object selects the first route object that matches the current request. (A route object is a class that implements RouteBase, and is typically an instance of the Route class.) If no routes match, the UrlRoutingModule object does nothing and lets the request fall back to the regular ASP.NET or IIS request processing.
From the selected Route object, the UrlRoutingModule object obtains the IRouteHandler object that is associated with the Route object. Typically, in an MVC application, this will be an instance of MvcRouteHandler. The IRouteHandler instance creates an IHttpHandler object and passes it the IHttpContext object. By default, the IHttpHandler instance for MVC is the MvcHandler object. The MvcHandler object then selects the controller that will ultimately handle the request.
A HttpModule plugs into the application lifecycle and can execute code before a controller has been instantiated or after it has been disposed, depending at which events it handles from the application lifecycle. So it can't use the exception handling of the controller.
If you want to handle the exception in the httpmodule properly I think you have 2 options:
- When you catch an exception you can alter the response of the request. You could insert an error div or replace the entire response with an error message or just log the exception. In this scenario the request will follow the rest of the pipeline.
- Or you can throw the exception and handle it in the global.asax Application_Error method. In this scenario the controller won't be executed.
If the HttpModule is throwing a valid exception then I would go for option 2. You can throw your own custom exception and handle it differently in the global.asax, even returning a HTTP response (400 Bad Request).
Update
If an exception is thrown in an HttpModule it will be caught by the Application_Error method. I tried following code snippet that works, although it feels not right:
protected void Application_Error()
{
var ex = Server.GetLastError(); // get the last exception that was made
if (ex == null) return; // if there is no exception, just continue
Response.ClearContent();
Response.Write("{'error':'" + ex.Message + "'}");
Response.ContentType = "application/json";
Response.Flush(); // flush the content to the client
Response.Close(); // and close the connection so that no other content can be written to the response
Server.ClearError(); // clear the error so that asp.net does not use the custom error page. If we don't close the response and clear the error, the request will still be handled in the rest of the pipeline.
}
精彩评论