5

I'm struggling to understand how to correctly handle errors in ASP.NET MVC4. As an example, I've created a new MVC4 project using the "Internet Application" template and updated my home controller to test out some error cases:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Message = "Hello";
        return View();
    }

    public ActionResult About()
    {
        throw new HttpException(401, "Not Authorized");
    }

    public ActionResult Contact()
    {
        throw new Exception("Oh no, some error occurred...");
    }
}

I have enabled customErrors in my web.config file:

<customErrors mode="On"></customErrors>

When I run the app and click "Contact", I see the ~/Views/Shared/Error.cshtml view as expected, since I have the HandleErrorAttribute registered as a global filter.

However, when I click "About", I get the standard ASP.NET yellow error page that says "Runtime Error". Why are these two exceptions being handled differently and how can I get instances of HttpException to get caught using the HandleError attribute?


CustomErrors config

Ideally, I'd like custom error pages for the following:

  • A custom 404 (not found) page that's nice and user friendly
  • A custom 401 (not authorised) page that informs the user that they do not have access (e.g. thrown after checking permissions for a particular item in the model)
  • A generic error page that is used in all other cases (in place of the standard yellow ASP.NET page).

I've created a new "Error" controller with views for each of the scenarios above. I have then updated customErrors in web.config like so:

<customErrors mode="On" defaultRedirect="~/Error/Trouble">
    <error statusCode="404" redirect="~/Error/NotFound"></error>
    <error statusCode="401" redirect="~/Error/NotAuthorized"></error>
</customErrors>

The 404 page works fine, but I don't get the 401 page at all. Instead, I get the ~/Error/Trouble view (the one specified as the defaultRedirect) when I try to access the About action on the Home controller.

Why is my custom 401 redirect page not working?

dove
  • 20,469
  • 14
  • 82
  • 108
Matt Wilson
  • 8,159
  • 8
  • 33
  • 55

3 Answers3

17

ASP.NET uses 401's internally to redirect users to the login page. Wherever you were planning to throw a 401 unauthorized, instead throw a 403 forbidden.

danludwig
  • 46,965
  • 25
  • 159
  • 237
  • That's a great (and very simple) solution, thanks! For the interest of anyone else reading this question, here's another good comment describing the difference between 401 and 403: http://stackoverflow.com/a/6937030/1145963 – Matt Wilson Nov 28 '12 at 15:14
8

If you really need to return a 401 and not a 403, then you can use:

HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true
Monstieur
  • 7,992
  • 10
  • 51
  • 77
5

I had a similar problem where I could not get 401 errors to go to my page despite the change to the web.config.

For a 401 you will probably be seeing the standard 401 Unauthorised page, even if you have added 401 to the customerrors section in your web.config. I read that when using IIS and Windows Authentication the check happens before ASP.NET even sees the request, hence you see it's own 401.

For my project I edited the Global.asax file to redirect to a route I had created for 401 errors, sending the user to the "Unauthorised to see this" view.

In the Global.asax:

    void Application_EndRequest(object sender, System.EventArgs e)
    {
        // If the user is not authorised to see this page or access this function, send them to the error page.
        if (Response.StatusCode == 401)
        {
            Response.ClearContent();
            Response.RedirectToRoute("ErrorHandler", (RouteTable.Routes["ErrorHandler"] as Route).Defaults);
        }
    }

and in the Route.config:

        routes.MapRoute(
        "ErrorHandler",
        "Error/{action}/{errMsg}",
        new { controller = "Error", action = "Unauthorised", errMsg = UrlParameter.Optional }
        );

and in the controller:

    public ViewResult Unauthorised()
    {
        //Response.StatusCode = 401; // Do not set this or else you get a redirect loop
        return View();
    }
VictorySaber
  • 3,084
  • 1
  • 27
  • 45