ChatGPT解决这个技术问题 Extra ChatGPT

Routing for custom ASP.NET MVC 404 Error page

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?

@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.
@pete This works as well stackoverflow.com/questions/619895/…
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.

A
Andrew Orsich

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+):

Configure custom pages in web config under system.web section: 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. 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; Set another magic setting in web config under system.webServer section:

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


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.
@Ek0nomik I've never seen ideal solutions :)
@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 up
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.
A
A-Sharabiani

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"

Thanks - that helped me out, 2 years later! What do you do if you only want a custom error for 404, nothing else?
@Shaul another year later... don't set the defaultRedirect, exclude the attribute altogether.
Y
Yasser Shaikh

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...

https://i.stack.imgur.com/YKPRh.png

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

https://i.stack.imgur.com/ozMbX.png

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.

https://i.stack.imgur.com/3S68S.png

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.


@niico From looking at the source code it appears to set the response code to 404, so yes.
K
KyleMit

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


+1. This is the only thing that seemed to work consistently for me on MVC5 with multiple areas.
This is the easiest solution. You can't use razor syntax in your error page though.
K
Keith

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>

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.
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.
C
Community

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?


I tried this however, I still get the ugly ASP.NET default Resource Not Found Message..
I don't find a definitive (and flexible!!) solution to this problem neither in this answer nor on the link provided.
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?
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.
k
kenorb

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.


S
SmokeIT

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


A
Andrus

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


F
Furkan ozturk

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>