5

I've struggled a couple of days with the error handling in MVC. I still do not have got it right. (I've also read most of the questions here at SO and googled until my fingers bled)

What I want to do:

  1. Use the standard [Authorize] attribute
  2. Redirect all errors to my error controller (including unauthorized)
  3. Have one action per HTTP error in my error controller.

What I do not want to do:

  1. Put the [ErrorHandler] on all of my controllers (can it be used on my base controller)?
  2. Use a custom Authorize attribute.

Actually I could do anything necessary (including the NOT list) as long as I get #1-3 working.

What I've tried:

  1. Using Application_Error
  2. Using Controller.HandleUnknownAction
  3. Using Controller.OnException
  4. Using [ErrorHandler] on my controllers
  5. Turning on/off CustomErrors in web.config

Guess I need a combination of those or maybe something else?

frennky
  • 12,581
  • 10
  • 47
  • 63
jgauffin
  • 99,844
  • 45
  • 235
  • 372

3 Answers3

4

You could also handle all your error logic in the Application_Error of your Global.asax.cs and then route them dynamically bases on the different HTTP status codes:

protected void Application_Error(object sender, EventArgs e)
{
    var ex = Server.GetLastError().GetBaseException();

    var routeData = new RouteData();

    if (ex.GetType() == typeof(HttpException))
    {
        var httpException = (HttpException)ex;

        switch (httpException.GetHttpCode())
        {
            case 401:
                routeData.Values.Add("action", "NotAuthorized");
                break;
            case 403:
                routeData.Values.Add("action", "NotAuthorized");
                break;
            case 404:
                routeData.Values.Add("action", "PageNotFound");
                break;
            default:
                routeData.Values.Add("action", "GeneralError");
                break;
        }
    }
    else
    {
        routeData.Values.Add("action", "GeneralError");
    }

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

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

It seems the 401 doesn't throw necessary a HttpException so you want to handle that manually to fit the logic:

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (Context.Response.StatusCode == 401)
    {
        throw new HttpException(401, "You are not authorised");
    }
}

And yes, your controllers inherit the attributes from your base conroller.

Martin Buberl
  • 45,844
  • 25
  • 100
  • 144
  • Tried that. It doesn't get called for 401. I have no idea why. – jgauffin Feb 03 '11 at 12:02
  • Haven't tried that for a 401, but according to this answer (http://stackoverflow.com/questions/1679881/asp-net-mvc-user-friendly-401-error) it should throw a `HttpException` as well. – Martin Buberl Feb 03 '11 at 12:05
  • Ah, it could be that you need to check for `Context.Response.StatusCode == 401` in your `Application_EndRequest()` and then throw the `HttpException(401, "You are not authorised")` by yourself. – Martin Buberl Feb 03 '11 at 12:07
2

You can use a custom filter, just extend ErrorHandlerAttribute and make it aware of your error controller. Than add it as a global filter inside Application_Start:

GlobalFilters.Filters.Add(new CustomAErrorHandlerAttribute());
frennky
  • 12,581
  • 10
  • 47
  • 63
0

The proper way is not to use Application_Error. The proper way is to use [HandleError] attribute in combination with the customErrors section in web.config as I describe here:

http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/

jgauffin
  • 99,844
  • 45
  • 235
  • 372