Error Handling in ASP.NET MVC

How can I properly handle exceptions thrown from controllers in ASP.NET MVC? The HandleError attribute seems to handle exceptions thrown by the MVC framework, rather than exceptions thrown by my own code.

Using this web.config

 <customErrors mode="On"> <error statusCode="401" redirect="/Errors/Http401" /> </customErrors> 

with the following code

 namespace MvcApplication1.Controllers { [HandleError] public class HomeController : Controller { public ActionResult Index() { // Force a 401 exception for testing throw new HttpException(401, "Unauthorized"); } } } 

doesn't lead to what i was hoping for. Instead, I get a generic ASP.NET error page telling me to modify my web.config to see the actual error information. However, if instead of throwing an exception, I return the wrong view, I get the page /Shared/Views/Error.aspx:

 return View("DoesNotExist"); 

Throwing exceptions in the controller, as I did above, seems to get around all the functionality of HandleError , so what is the correct way to create error pages and how do I fit in well with the MVC framework?

+63
exception asp.net-mvc
May 01 '09 at 4:58 p.m.
source share
8 answers

Thanks to kazimanzurrashaid, here is what I did in Global.asax.cs:

 protected void Application_Error() { Exception unhandledException = Server.GetLastError(); HttpException httpException = unhandledException as HttpException; if (httpException == null) { Exception innerException = unhandledException.InnerException; httpException = innerException as HttpException; } if (httpException != null) { int httpCode = httpException.GetHttpCode(); switch (httpCode) { case (int) HttpStatusCode.Unauthorized: Response.Redirect("/Http/Error401"); break; } } } 

I will be able to add additional pages to the HttpContoller based on any additional HTTP error codes I need to support.

+20
May 01 '09 at 19:51
source share

Controller.OnException(ExceptionContext context) . Override it.

 protected override void OnException(ExceptionContext filterContext) { // Bail if we can't do anything; app will crash. if (filterContext == null) return; // since we're handling this, log to elmah var ex = filterContext.Exception ?? new Exception("No further information exists."); LogException(ex); filterContext.ExceptionHandled = true; var data = new ErrorPresentation { ErrorMessage = HttpUtility.HtmlEncode(ex.Message), TheException = ex, ShowMessage = !(filterContext.Exception == null), ShowLink = false }; filterContext.Result = View("ErrorPage", data); } 
+63
May 01, '09 at 17:32
source share

The HandleError attribute, apparently, handles only the exceptions thrown by the MVC framework, not the exceptions thrown by my own code.

This is simply wrong. Indeed, HandleError will only "handle" exceptions, either thrown into your own code, or into the code called by your own code. In other words, there are only exceptions when your action is on the call stack.

The real explanation for the behavior you see is the specific exception that you throw. HandleError behaves differently with an HttpException. From the source code:

  // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), // ignore it. if (new HttpException(null, exception).GetHttpCode() != 500) { return; } 
+13
May 01 '09 at 17:24
source share

I donโ€™t think you can show a specific HttpCode-based error with the HandleError attribute, and I would rather use an HttpModule for this purpose. Assuming I have a โ€œErrorPagesโ€ folder, where there is a different page for each specific error, and the mapping is set in web.config in the same way as a regular web form application. Below is the code used to display the error page:

 public class ErrorHandler : BaseHttpModule{ public override void OnError(HttpContextBase context) { Exception e = context.Server.GetLastError().GetBaseException(); HttpException httpException = e as HttpException; int statusCode = (int) HttpStatusCode.InternalServerError; // Skip Page Not Found and Service not unavailable from logging if (httpException != null) { statusCode = httpException.GetHttpCode(); if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable)) { Log.Exception(e); } } string redirectUrl = null; if (context.IsCustomErrorEnabled) { CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors"); if (section != null) { redirectUrl = section.DefaultRedirect; if (httpException != null) { if (section.Errors.Count > 0) { CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)]; if (item != null) { redirectUrl = item.Redirect; } } } } } context.Response.Clear(); context.Response.StatusCode = statusCode; context.Response.TrySkipIisCustomErrors = true; context.ClearError(); if (!string.IsNullOrEmpty(redirectUrl)) { context.Server.Transfer(redirectUrl); } } 

}

+6
May 01 '09 at 18:52
source share

Another possibility (not true in your case) that others reading this may experience is that your error page itself throws an error or does not implement it:

  System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo> 

If so, you will get a page with a default error (otherwise you will get an infinite loop because it will try to send itself to the user error page). It didnโ€™t seem to me right away.

This model is a model sent to an error page. If your error page uses the same main page as the rest of your site, and it requires any other model information, you need to either create your own attribute of type [HandleError] , or override OnException or something else.

+3
May 20 '09 at 2:09
source share

I chose the Controller.OnException () method, which is a logical choice for me - since I chose ASP.NET MVC, I prefer to stay at the structural level and avoid messing with the underlying mechanics, if possible.

I ran into the following problem:

If an exception occurs in the view, partial output from that view will appear on the screen along with the error message.

I fixed this by clearing the answer before setting filterContext.Result - like this:

  filterContext.HttpContext.Response.Clear(); // gets rid of any garbage filterContext.Result = View("ErrorPage", data); 

Hope this saves you some time :-)

+2
Aug 10 '10 at 19:29
source share
  protected override void OnException (ExceptionContext filterContext ) { if (filterContext != null && filterContext.Exception != null) { filterContext.ExceptionHandled = true; this.View("Error").ViewData["Exception"] = filterContext.Exception.Message; this.View("Error").ExecuteResult(this.ControllerContext); } } 
+2
Oct 10 2018-11-12T00:
source share

Jeff Atwood Custom exception handling module great for MVC. You can fully configure it in your web.config without any changes to the source code of the MVC project. However, a small modification is required to change the original HTTP status instead of status 200 . See this related forum .

Basically, in Handler.vb you can add something like:

 ' In the header... Private _exHttpEx As HttpException = Nothing ' At the top of Public Sub HandleException(ByVal ex As Exception)... HttpContext.Current.Response.StatusCode = 500 If TypeOf ex Is HttpException Then _exHttpEx = CType(ex, HttpException) HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode() End If 
+1
Apr 23 '12 at 19:35
source share



All Articles