Cédric Boivin
Cédric Boivin

Reputation: 11351

How to manage all unhandled errors in MVC controller

I try in Global.asax

protected void Application_Start()
    {
      AreaRegistration.RegisterAllAreas();

      RegisterGlobalFilters(GlobalFilters.Filters);
      RegisterRoutes(RouteTable.Routes);
      this.Error += new EventHandler(MvcApplication_Error);
    }

    public void MvcApplication_Error(object sender, EventArgs e)
    {
      throw new NotImplementedException();
    }

But the Event is never raised !

Upvotes: 2

Views: 2578

Answers (4)

Phate01
Phate01

Reputation: 1795

In order to make it work you have to register the event in the MvcApplication constructor:

public MvcApplication()
{
    Error += MvcApplication_Error;
}

private void MvcApplication_Error(object sender, EventArgs e)
{
    //Do your stuff
}

Upvotes: 0

Nicholas Carey
Nicholas Carey

Reputation: 74277

The easiest way to deal with unhandled exceptions gracefully is to (A) use Elmah to hook unhandled exceptions and log them, and (B) trap the unhandled exception with the global.asax's Application_Error() method. Here's mine:

    protected void Application_Error( object sender , EventArgs e )
    {
        // only do something special if the request is not from the local machine.
        if ( !Request.IsLocal )
        {
            Exception     exception     = Server.GetLastError() ;
            HttpException httpException = exception as HttpException ;
            RouteData     routeData     = new RouteData() ;

            routeData.Values.Add( "controller" , "Error" ) ;

            if ( httpException != null )
            {
                int httpStatusCode = httpException.GetHttpCode() ;

                switch ( httpStatusCode )
                {
                case 404 : routeData.Values.Add( "action" , "Http404FileNotFound"        ) ; break ;
                default  : routeData.Values.Add( "action" , "HttpError"                  ) ; break ;
                }
                routeData.Values.Add( "exception" , httpException ) ;
            }
            else
            {
                routeData.Values.Add( "action" , "Http500InternalServerError" ) ;
                routeData.Values.Add( "exception" , exception ) ;
            }

            Response.Clear() ;
            Server.ClearError() ;

            HttpContextBase contextWrapper  = new HttpContextWrapper( Context ) ;
            RequestContext  newContext      = new RequestContext( contextWrapper , routeData ) ;
            IController     errorController = new ErrorController() ;
            errorController.Execute( newContext ) ;
        }

        return ;
    }

The above method:

  • for local requests (e.g., the developer's machine), the exception is not handled and ASP.Net hands back the standard Yellow Screen O'Death.

  • handles remote requests by displaying (not redirecting -- the user sees the original URL) a friendly error page that doesn't leak implementation information (like stack traces, etc.) The logic goes like this:

    • If the exception can be cast to an HttpException, then the HTTP status (response) code is examined. An Http status code of 404 displays our "Resource Not Found" view, with a 404 response status; other HttpExceptions get a different "Http Error" view that sets the Response Status to 500 (Internal Server Error).

    • If the exception cannot be cast to an HttpException, then an "Internal Error" view is returned, which also sets the Response status to 500 (Internal Server Error).

Easy! You might want to use log4net where appropriate (say, at the point the exception is thrown) to log any contextual data that might help debug the root cause of the exception.

Upvotes: 8

Jace Rhea
Jace Rhea

Reputation: 5018

In addition to Nico's suggestion you can use an attribute on the controller such as this...

    [ExceptionHandler("DefaultError", typeof(DivideByZeroException))]
public class HomeController : N2Controller
{
   [ControllerAction, ExceptionHandler("Error")]
   public void Index()
   {

   }
}

Example source.

This allows you to have have more granular control (per method) of the exception handler if required or just have a single attribute in your base controller that will handle all errors.

Upvotes: 2

Nico
Nico

Reputation: 496

You can use the OnException method that is already in Controller MetaData

protected override void OnException(ExceptionContext filterContext)
    {
      //Build of error source.
      string askerUrl = filterContext.RequestContext.HttpContext.Request.RawUrl;
      Exception exToLog = filterContext.Exception;
      exToLog.Source = string.Format("Source : {0} {1}Requested Path : {2} {1}", exToLog.Source, Environment.NewLine, askerUrl);
      //Log the error
      ExceptionLogger.Publish(exToLog, "unhandled", filterContext.RequestContext.HttpContext.Request.ServerVariables.ToString(), askerUrl);
      //Redirect to error page : you must feed filterContext.Result to cancel already executing Action
      filterContext.ExceptionHandled = true;
      filterContext.Result = new ErrorController().ManageError(ErrorResources.GeneralErrorPageTitle, ErrorResources.GeneralErrorTitle, ErrorResources.GeneralErrorInnerBody, exToLog);
      base.OnException(filterContext);
    }

Upvotes: 7

Related Questions