ASP.NET MVC Custom Error page (StatusCode 404 throws a 500)

I’ve got customErrors set in my web.config

<customErrors mode="On" defaultRedirect="/Error/GeneralError">
        <error statusCode="404" redirect="/Error/NotFound"/>
    </customErrors>

This works fine locally. A 404 throws a 404. On the shared hosting it throws up the standard server 404 page unless I specifically set 404 to point to /Error/NotFound. That’s fine. Now it will show the custom 404 page except the response status code is 200. So if I try to throw Response.StatusCode = 404; in my NotFound action in ErrorController like this:

public class ErrorController : Controller
    {
        public ActionResult NotFound()
        {
            Response.StatusCode = 404;
            return View();
        }
    }

the server throws a status code 500 Internal Server Error but my GeneralError page doesn’t show, just a blank white page with no source.

I’ve tried many different combinations but I can’t seem to find how to make it show my custom 404 page along with a 404 response.

Any ideas?

Ah! A friend pointed me to Response.TrySkipIisCustomErrors = true; which seems to allow me to both send a Response.StatusCode = 404; and use my custom error page. Perfect! :slight_smile:

I guess what was happening was my Error/NotFound was sending a 404 in the response header which was causing the server to throw a 404 which was pointing to my Error/NotFound which was causing the server to throw a 404 which was pointing to my Error/NotFound which was …

endless loop = 500 Internal Server Error.

Mark, considering this is an mvc app, you may want to look into the HandleError() attribute. Instead of setting a StatusCode, and then returning a call to view, do this instead.

[HandleError(ExceptionType = typeof(CustomException), View = “SomeErrorView”)]
public class TestController : Controller
{

public ActionResult DoSomething()
{

// normal processing

if (someCondition) { throw new CustomException(); }

// normal processing

return View();

}

}

Now whenever you throw CustomException the SomeErrorView view will be displayed immediately, and execution is halted. The second “normal processing” line is never reached. This allows you to control what views get displayed for each type of error without having the need to write an action just to display that view.

Hope this helps somewhat.

Hmm. I wasn’t that savvy on the [HandleError]. I do have it in place but without the attributes (ExceptionType= and View=)

Doesn’t that still put a “200 OK” in the page’s response header though?

I don’t like the attributes solution… you eventualy end up spreading them all over the controllers… have you guys looked at dhtmlgold pet project? Great for this stuff.

The issue is solved by setting Response.TrySkipIisCustomErrors = true; and it seems correct, not a work around.

Pufa is correct and I think I missunderstand Mark’s intent. Is the NotFound action a default action by any chance? Handling 404’s this way seems a bit wrong altogether. Simply don’t handle it. In production, and debug=false etc, IIS should report a 404 correctly. Is it my understanding that it is not doing this?

my sujestion was for the “[HandleError(ExceptionType = typeof(CustomException), View = “SomeErrorView”)]” not the 404

If you don’t handle the 404 the generic IIS 404 page shows and not the custom page. Right?

Right. So treat it like any other website, mvc, webforms, or otherwise. Just create the following action and view, and define the resource in IIS itself. So if IIS can’t find a page, and you have /MyApp/Error/Custom404 mapped as the resource url for 404’s, then it will show.

public class ErrorController : Controller
{
public ActionResult Custom404() { return View(); }
}

What I mean by the “generic 404” is the one that is stock to IIS. Unstyled general “The page cannot be found. The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.” text, not my custom 404. Sending a 404 to IIS7 generates this page under shared hosting no matter the web.config settings. At least that’s what I’m running into without using TrySkipIisCustomErrors.