Routing for custom ASP.NET MVC 404 Error page
T

10

122

I am trying to make a custom HTTP 404 error page when someone types in a URL that doesn't invoke a valid action or controller in ASP.NET MVC, instead of it displaying the generic "Resource Not Found" ASP.NET error.

I don't want to use the web.config to handle this.

Is there any kind of routing magic I can do to catch any invalid URLs?

Update: I tried the answer given, however I still get the ugly "Resource Not Found" message.

Another update: Ok, apparently something changed in RC1. I've even tried specifically trapping 404 on an HttpException and it still just gives me the "Resource Not Found" page.

I've even used MvcContrib's resource's feature and nothing - same problem. Any ideas?

Tyrolienne answered 16/2, 2009 at 16:37 Comment(7)
Duplicate #311080Acidic
@Peter That is a the solution I added that along with overriding the HandleUnknownAction to show a page not found view when an action doesn't exist and then the built in ASP.net custom error handler to handle anything else wonky that users may type in.Tyrolienne
@pete This works as well #620395Tyrolienne
I hate it when other users are presumptuous and say stuff like, "why would you want to do that? Just do this..." But I'd suggest that if there is nothing stopping you from using the web.config approach and it satisfies your needs, it is a standard and elegant approach. Any feedback on that approach is appreciated, as there very well could be some issue with it that I don't know about.Predictor
Related: How can I properly handle 404 in ASP.NET MVC?Hagan
See: Custom Error Page with ASP.Net MVC 4Hagan
This is the duplicated question, as the main accepted answer actually points to that page, so it's kind of link-only answer without providing any meaningful solution.Hagan
A
11

Just add catch all route at the end of the routes table and display whatever page you want with it.

See: How can i make a catch all route to handle '404 page not found' queries for ASP.NET MVC?

Afflict answered 16/2, 2009 at 17:40 Comment(4)
I tried this however, I still get the ugly ASP.NET default Resource Not Found Message..Tyrolienne
I don't find a definitive (and flexible!!) solution to this problem neither in this answer nor on the link provided.Needlepoint
Neither do I...this seems like kind of a hacky answer. I'm looking more for a way to make customErrors behave like they do in WebForms. Any ideas?Anklet
This both doesn't work & is bad on principle. Doesn't work because it won't catch bad URLs that do match one of the earlier rout patterns. Bad on principle because it converts what should be an error to not an error. Redirecting to a page you've named "Error" is different than redirecting to an error page. You want to keep it as an error, log the error, then handle it as an error. Errors are valuable information.Phytography
C
111

I've tried to enable custom errors on production server for 3 hours, seems I found final solution how to do this in ASP.NET MVC without any routes.

To enable custom errors in ASP.NET MVC application we need (IIS 7+):

  1. Configure custom pages in web config under system.web section:

    <customErrors mode="RemoteOnly"  defaultRedirect="~/error">
        <error statusCode="404" redirect="~/error/Error404" />
        <error statusCode="500" redirect="~/error" />
    </customErrors>
    

    RemoteOnly means that on local network you will see real errors (very useful during development). We can also rewrite error page for any error code.

  2. Set magic Response parameter and response status code (in error handling module or in error handle attribute)

      HttpContext.Current.Response.StatusCode = 500;
      HttpContext.Current.Response.TrySkipIisCustomErrors = true;
    
  3. Set another magic setting in web config under system.webServer section:

    <httpErrors errorMode="Detailed" />
    

This was final thing that I've found and after this I can see custom errors on production server.

Cranky answered 16/9, 2011 at 8:58 Comment(4)
This still isn't an ideal solution. It doesn't get you proper HTTP response codes. Using customErrors results in a 302 redirect just to load up your error page.Agrapha
@Ek0nomik I've never seen ideal solutions :)Cranky
@JustinHelgerson I setup an ErrorController for each status code and set the Response Codes inside those actions and it sets the response codes fine.. I tested with Fiddler and 404 status codes respond with a 404 and pages with 500 errors respond with a 500. And in both cases my custom error views are served upVillain
my team mate just deleted Error view under Shared folder, to make runtime error with 500 status code to redirect to Custom Error Page as mentioned here and here. Hope helps someone.Inquisitive
G
42

I got my error handling to work by creating an ErrorController that returns the views in this article. I also had to add the "Catch All" to the route in global.asax.

I cannot see how it will get to any of these error pages if it is not in the Web.config..? My Web.config had to specify:

customErrors mode="On" defaultRedirect="~/Error/Unknown"

and then I also added:

error statusCode="404" redirect="~/Error/NotFound"
Ganny answered 17/8, 2009 at 16:0 Comment(2)
Thanks - that helped me out, 2 years later! What do you do if you only want a custom error for 404, nothing else?Temporize
@Shaul another year later... don't set the defaultRedirect, exclude the attribute altogether.Menstruum
I
30

Source

NotFoundMVC - Provides a user-friendly 404 page whenever a controller, action or route is not found in your ASP.NET MVC3 application. A view called NotFound is rendered instead of the default ASP.NET error page.

You can add this plugin via nuget using: Install-Package NotFoundMvc

NotFoundMvc automatically installs itself during web application start-up. It handles all the different ways a 404 HttpException is usually thrown by ASP.NET MVC. This includes a missing controller, action and route.

Step by Step Installation Guide :

1 - Right click on your Project and Select Manage Nuget Packages...

2 - Search for NotFoundMvc and install it. enter image description here

3 - Once the installation has be completed, two files will be added to your project. As shown in the screenshots below.

enter image description here

4 - Open the newly added NotFound.cshtml present at Views/Shared and modify it at your will. Now run the application and type in an incorrect url, and you will be greeted with a User friendly 404 page.

enter image description here

No more, will users get errors message like Server Error in '/' Application. The resource cannot be found.

Hope this helps :)

P.S : Kudos to Andrew Davey for making such an awesome plugin.

Inaudible answered 3/11, 2012 at 12:7 Comment(1)
@niico From looking at the source code it appears to set the response code to 404, so yes.Fifteen
E
23

Try this in web.config to replace IIS error pages. This is the best solution I guess, and it sends out the correct status code too.

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="-1" />
    <remove statusCode="500" subStatusCode="-1" />
    <error statusCode="404" path="Error404.html" responseMode="File" />
    <error statusCode="500" path="Error.html" responseMode="File" />
  </httpErrors>
</system.webServer>

More info from Tipila - Use Custom Error Pages ASP.NET MVC

Ermin answered 15/3, 2012 at 6:20 Comment(2)
+1. This is the only thing that seemed to work consistently for me on MVC5 with multiple areas.Resentment
This is the easiest solution. You can't use razor syntax in your error page though.Afford
C
16

This solution doesn't need web.config file changes or catch-all routes.

First, create a controller like this;

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Title = "Regular Error";
        return View();
    }

    public ActionResult NotFound404()
    {
        ViewBag.Title = "Error 404 - File not Found";
        return View("Index");
    }
}

Then create the view under "Views/Error/Index.cshtml" as;

 @{
      Layout = "~/Views/Shared/_Layout.cshtml";
  }                     
  <p>We're sorry, page you're looking for is, sadly, not here.</p>

Then add the following in the Global asax file as below:

protected void Application_Error(object sender, EventArgs e)
{
        // Do whatever you want to do with the error

        //Show the custom error page...
        Server.ClearError(); 
        var routeData = new RouteData();
        routeData.Values["controller"] = "Error";

        if ((Context.Server.GetLastError() is HttpException) && ((Context.Server.GetLastError() as HttpException).GetHttpCode() != 404))
        {
            routeData.Values["action"] = "Index";
        }
        else
        {
            // Handle 404 error and response code
            Response.StatusCode = 404;
            routeData.Values["action"] = "NotFound404";
        } 
        Response.TrySkipIisCustomErrors = true; // If you are using IIS7, have this line
        IController errorsController = new ErrorController();
        HttpContextWrapper wrapper = new HttpContextWrapper(Context);
        var rc = new System.Web.Routing.RequestContext(wrapper, routeData);
        errorsController.Execute(rc);

        Response.End();
}

If you still get the custom IIS error page after doing this, make sure the following sections are commented out(or empty) in the web config file:

<system.web>
   <customErrors mode="Off" />
</system.web>
<system.webServer>   
   <httpErrors>     
   </httpErrors>
</system.webServer>
Cassock answered 19/3, 2013 at 21:58 Comment(2)
I don't think this approach will work in all instances, since a 404 is often intercepted by the web server and therefore never handled at application level.Dissolve
I want to add that if you go with this approach, I would recommend adding a Response.End(); to the end of your Application_Error handler. Without this, only the first occurrence of an error is triggered for a given session, which is an issue when you have, for example, custom error logging in the Application_Error handler.Suasion
A
11

Just add catch all route at the end of the routes table and display whatever page you want with it.

See: How can i make a catch all route to handle '404 page not found' queries for ASP.NET MVC?

Afflict answered 16/2, 2009 at 17:40 Comment(4)
I tried this however, I still get the ugly ASP.NET default Resource Not Found Message..Tyrolienne
I don't find a definitive (and flexible!!) solution to this problem neither in this answer nor on the link provided.Needlepoint
Neither do I...this seems like kind of a hacky answer. I'm looking more for a way to make customErrors behave like they do in WebForms. Any ideas?Anklet
This both doesn't work & is bad on principle. Doesn't work because it won't catch bad URLs that do match one of the earlier rout patterns. Bad on principle because it converts what should be an error to not an error. Redirecting to a page you've named "Error" is different than redirecting to an error page. You want to keep it as an error, log the error, then handle it as an error. Errors are valuable information.Phytography
G
6

If you work in MVC 4, you can watch this solution, it worked for me.

Add the following Application_Error method to my Global.asax:

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Server.ClearError();

    RouteData routeData = new RouteData();
    routeData.Values.Add("controller", "Error");
    routeData.Values.Add("action", "Index");
    routeData.Values.Add("exception", exception);

    if (exception.GetType() == typeof(HttpException))
    {
        routeData.Values.Add("statusCode", ((HttpException)exception).GetHttpCode());
    }
    else
    {
        routeData.Values.Add("statusCode", 500);
    }

    IController controller = new ErrorController();
    controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    Response.End();

The controller itself is really simple:

public class ErrorController : Controller
{
    public ActionResult Index(int statusCode, Exception exception)
    {
        Response.StatusCode = statusCode;
        return View();
    }
}

Check the full source code of Mvc4CustomErrorPage at GitHub.

Geerts answered 25/5, 2015 at 8:30 Comment(0)
C
0

I had the same problem, the thing you have to do is, instead of adding the customErrors attribute in the web.config file in your Views folder, you have to add it in the web.config file in your projects root folder

Clamper answered 27/12, 2009 at 15:35 Comment(0)
B
0

Here is true answer which allows fully customize of error page in single place. No need to modify web.config or create separate code.

Works also in MVC 5.

Add this code to controller:

        if (bad) {
            Response.Clear();
            Response.TrySkipIisCustomErrors = true;
            Response.Write(product + I(" Toodet pole"));
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            //Response.ContentType = "text/html; charset=utf-8";
            Response.End();
            return null;
        }

Based on http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages

Blockhouse answered 22/7, 2016 at 16:13 Comment(0)
B
0

I will talk about some specific cases,

if you are using 'PageNotFound method' in HomeController like below

[Route("~/404")]
public ActionResult PageNotFound()
{
  return MyView();
}

it wouldn't work this. But you must clear Route tags like below,

//[Route("~/404")]
public ActionResult PageNotFound()
{
  return MyView();
}

And if you change it as method Name in web.config it works. However don't forget to do code like below in web.config

<customErrors mode="On">
  <error statusCode="404" redirect="~/PageNotFound" /> 
 *// it is not "~/404" because it is not accepted url in Route Tag like [Route("404")]*
</customErrors>
Beauvais answered 11/3, 2019 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.