MVC 6 404 Not Found

2020-02-01 04:24发布

问题:

Instead of getting a HTTP 404 response, I'd like to have a generic 404 Not Found page (HTTP 200). I know you can set that up in MVC 5 with

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

But I can't seem to figure out how to do this in MVC 6. I'm guessing it should be in either the UseMvc routing table, or a custom middleware.

回答1:

Startup.cs:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // PICK YOUR FLAVOR.

        // app.UseErrorPage(ErrorPageOptions.ShowAll);
        app.UseStatusCodePages(); // There is a default response but any of the following can be used to change the behavior.

        // app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
        // app.UseStatusCodePages("text/plain", "Response, status code: {0}");
        // app.UseStatusCodePagesWithRedirects("~/errors/{0}"); // PathBase relative
        // app.UseStatusCodePagesWithRedirects("/base/errors/{0}"); // Absolute
        // app.UseStatusCodePages(builder => builder.UseWelcomePage());
        // app.UseStatusCodePagesWithReExecute("/errors/{0}");
    }
} 

project.json:

"dependencies": {
    "Microsoft.AspNet.Diagnostics": "1.0.0-*",
    // ....
}

note that, if you are hosting on IIS (or azure), the web.config does still works and it's big maybe needed in some hosting scenarios. (it should be placed inside wwwroot folder



回答2:

You can handle 404 errors with the UseStatusCodePagesWithReExecute but you need to set the status code at the end of your configure pipeline first.

public void Configure(IApplicationBuilder app)
    {
        app.UseIISPlatformHandler();

        if (string.Equals(_env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
        {
            app.UseDeveloperExceptionPage();
            app.UseRuntimeInfoPage();
        } else
        {
            app.UseExceptionHandler("/Error");
        }
        app.UseStatusCodePagesWithReExecute("/Error/Status/{0}");

        app.UseStaticFiles();

        app.UseMvc();

        app.Use((context, next) =>
        {
            context.Response.StatusCode = 404;
            return next();
        });
    }

This will set the status code to 404 if nothing in the pipeline can handle it (which is what you want).

Then you can easily capture and handle the response any way you want in your controller.

[Route("error")]
public class ErrorController : Controller
{

[Route("Status/{statusCode}")]
    public IActionResult StatusCode(int statusCode)
    {

            //logic to generate status code response

            return View("Status", model);
    }
}


回答3:

If anyone is looking to create custom 404 pages with HTML, you can do this:

Startup.cs

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {        
        app.UseStatusCodePagesWithReExecute("/errors/{0}");
    }
}

project.json

"dependencies": {
    "Microsoft.AspNet.Diagnostics": "1.0.0-*",
    // ....
}

ErrorsController.cs

public class ErrorsController : Controller
{
    // ....

    [Route("/Error/{statusCode}")]
    public IActionResult ErrorRoute(string statusCode)
    {
        if (statusCode == "500" | statusCode == "404")
        {
            return View(statusCode);
        }

        return View("~/Views/Errors/Default.cshtml");
    }
}

All that's left after this is to create a 404.cshtml, 500.cshtml, and Default.cshtml in your Errors view folder and customize the HTML to your liking.



回答4:

In the Configure function of the Startup class, call:

app.UseErrorPage(ErrorPageOptions.ShowAll);

more production ready:

    if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
    {
        app.UseBrowserLink();
        app.UseErrorPage(ErrorPageOptions.ShowAll);
        app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
    }
    else
    {
        // Add Error handling middleware which catches all application specific errors and
        // send the request to the following path or controller action.
        app.UseErrorHandler("/Home/Error");
    }


回答5:

There is a middleware called StatusCodePagesMiddleware which I believe was added post-Beta3.

Sample: https://github.com/aspnet/Diagnostics/blob/dev/samples/StatusCodePagesSample/Startup.cs



回答6:

If you have a standard ASP.NET 5 Web application template project you can get a really nice solution that will handle both keeping the error status code at the same time as you serve the cshtml file of your choice.

Startup.cs, in the Configure method

Replace

app.UseExceptionHandler("/Error");

with

app.UseStatusCodePagesWithReExecute("/Home/Errors/{0}");

Then remove the following catchall:

app.Run(async (context) => 
{ 
   var logger = loggerFactory.CreateLogger("Catchall Endpoint"); 
   logger.LogInformation("No endpoint found for request {path}", context.Request.Path); 
   await context.Response.WriteAsync("No endpoint found - try /api/todo."); 
});

In HomeController.cs

Replace the current Error method with the following one:

public IActionResult Errors(string id) 
{ 
  if (id == "500" | id == "404") 
  { 
    return View($"~/Views/Home/Error/{id}.cshtml"); 
  }

  return View("~/Views/Shared/Error.cshtml"); 
}

That way you can add error files you feel the users can benefit from (while search engines and such still get the right status codes)

Now add a folder named Error in ~Views/Home folder Create two .cshtml files. Name the first one 404.cshtml and the second one 500.cshtml.

Give them some useful content and the start the application and write something like http://localhost:44306/idonthaveanendpointhere and confirm the setup works.

Also press F12 developer tools and confirm the status code is 404.