问题 ASP.NET MVC 6基于HTTP状态代码处理错误


我想为每个状态代码显示不同的错误消息,例如:

  • 400错误请求
  • 403禁止
  • 500内部服务器错误
  • 404未找到
  • 401未经授权

如何在新的ASP.NET MVC 6应用程序中实现这一目标?我可以使用内置的UseErrorHandler方法执行此操作吗?

application.UseErrorHandler("/error");

另外,我注意到即使使用上述处理程序,也输入一个不存在的URL,例如/ this-page-does-not-exist,导致IIS出现丑陋的404 Not Found错误页面。怎么还能处理?

在MVC 5中,我们不得不使用ASP.NET的system.web customerrors部分  web.config文件中的system.webServer httpErrors部分,但很难处理笨拙,有很多非常奇怪的行为。 MVC 6能让这更简单吗?


9865
2018-06-11 16:12


起源



答案:


你可以使用 StatusCodePagesMiddleware 为了这。以下是一个例子:

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    app.UseStatusCodePagesWithReExecute("/StatusCodes/StatusCode{0}");

    app.UseMvcWithDefaultRoute();

处理状态代码请求的控制器:

public class StatusCodesController : Controller
{
    public IActionResult StatusCode404()
    {
        return View(viewName: "NotFound"); // you have a view called NotFound.cshtml
    }

    ... more actions here to handle other status codes
}

一些说明:

  • 检查其他扩展方法,例如 UseStatusCodePagesWithRedirectsUseStatusCodePages 用于其他功能。
  • 我在我的示例中尝试将StatusCode作为查询字符串,但看起来像这样 中间件不处理查询字符串,但你可以看看 这个 代码并解决此问题。

11
2018-06-11 19:36



注意:这是一个比我在下面提供的更好的解决方案。 - N. Taylor Mullen
此代码针对所有请求执行。我是否正确地说这将被添加到可以传递给UseErrorHandler的Action委托中? - Muhammad Rehan Saeed
@RehanSaeed:是的,这可能会针对所有请求执行,因为它需要拦截响应以验证状态代码。我不清楚你的第二个问题。这是一个不同的中间件 UserErrorHandler。你能详细说明吗? - Kiran Challa


答案:


你可以使用 StatusCodePagesMiddleware 为了这。以下是一个例子:

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    app.UseStatusCodePagesWithReExecute("/StatusCodes/StatusCode{0}");

    app.UseMvcWithDefaultRoute();

处理状态代码请求的控制器:

public class StatusCodesController : Controller
{
    public IActionResult StatusCode404()
    {
        return View(viewName: "NotFound"); // you have a view called NotFound.cshtml
    }

    ... more actions here to handle other status codes
}

一些说明:

  • 检查其他扩展方法,例如 UseStatusCodePagesWithRedirectsUseStatusCodePages 用于其他功能。
  • 我在我的示例中尝试将StatusCode作为查询字符串,但看起来像这样 中间件不处理查询字符串,但你可以看看 这个 代码并解决此问题。

11
2018-06-11 19:36



注意:这是一个比我在下面提供的更好的解决方案。 - N. Taylor Mullen
此代码针对所有请求执行。我是否正确地说这将被添加到可以传递给UseErrorHandler的Action委托中? - Muhammad Rehan Saeed
@RehanSaeed:是的,这可能会针对所有请求执行,因为它需要拦截响应以验证状态代码。我不清楚你的第二个问题。这是一个不同的中间件 UserErrorHandler。你能详细说明吗? - Kiran Challa


如何在新的ASP.NET MVC 6应用程序中实现这一目标?我可以使用内置的UseErrorHandler方法执行此操作吗?

快速回答: 不是优雅的方式。

说明/另类: 首先来看看是什么的 UseErrorHandler 方法实际上在做: https://github.com/aspnet/Diagnostics/blob/6dbbe831c493e6e7259de81f83a04d1654170137/src/Microsoft.AspNet.Diagnostics/ErrorHandlerExtensions.cs#L25 它添加了以下中间件: https://github.com/aspnet/Diagnostics/blob/6dbbe831c493e6e7259de81f83a04d1654170137/src/Microsoft.AspNet.Diagnostics/ErrorHandlerMiddleware.cs  注意 第29-78行(调用方法)

每当请求进入时执行invoke方法(由您的位置控制) application.UseErrorHandler("...") 在你的 Startup.cs)。所以 UseErrorHandler 是一种增加自定义中间件的美化方式:中间件=可以作用于http请求的组件。

现在有了这样的背景,如果我们想添加我们自己的差异化请求的错误中间件。我们可以通过添加类似默认的类似中间件来做到这一点 ErrorHandlerMiddleware 通过修改这些行: https://github.com/aspnet/Diagnostics/blob/6dbbe831c493e6e7259de81f83a04d1654170137/src/Microsoft.AspNet.Diagnostics/ErrorHandlerMiddleware.cs#L48-L51 通过这种方法,我们可以根据状态代码控制重定向路径。

在MVC 5中,我们不得不使用ASP.NET的system.web customerrors部分和web.config文件中的system.webServer httpErrors部分,但很难处理笨拙的行为,有很多非常奇怪的行为。 MVC 6能让这更简单吗?

回答: 肯定会:)。就像上面的答案一样,修复在于添加中间件。有一个快捷方式通过添加简单的中间件 IApplicationBuilder 在你的 Startup.cs;在你的结尾 Configure 方法可以添加以下内容:

app.Run(async (context) =>
{
    await context.Response.WriteAsync("Could not handle the request.");

    // Nothing else will run after this middleware.
});

这将起作用,因为这意味着您到达了http管道的末尾而没有处理请求(因为它在您的末尾) Configure 方法 Startup.cs)。如果你想添加这个中间件(以快速方式)和你之后执行中间件的选项,请按以下步骤操作:

app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Could not handle the request.");

    // This ensures that any other middelware added after you runs.
    await next();
});

希望这可以帮助!


3
2018-06-11 18:20



据推测,这只是web.config的customErrors部分的替代品,不适用于404未找到的错误。为此,我们仍然需要使用httpErrors? - Muhammad Rehan Saeed
这也适用于404未找到的错误。当没有任何东西可以处理请求时,会发生404。 - N. Taylor Mullen


使用各种状态代码,而无需在控制器中单独指定每个状态代码。

Startup.cs:

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    app.UseStatusCodePagesWithRedirects("/StatusCodes?statusCode={0}");
    app.UseMvcWithDefaultRoute();

控制器:

  public class StatusCodesController : Controller
  {
    public IActionResult Index(string statusCode)
    {
      if(statusCode == null) statusCode = "";
      if(statusCode == "404") return View("Error404");
      return View("Index",statusCode);
    }

    public IActionResult Test404() { return StatusCode(404); }
    public IActionResult Test500() { return StatusCode(500); }

视图:

@model string
@{ ViewData["Title"] = Model + " Oops!"; }

<style>
  .error-template {
    padding: 40px 15px;
    text-align: center;
  }

  .error-actions {
    margin-bottom: 15px;
    margin-top: 15px;
  }

    .error-actions .btn {
      margin-right: 10px;
    }
</style>

<div class="container">
  <div class="row">
    <div class="col-md-12">
      <div class="error-template">
        <h2>Oops!</h2>
        <h2>@Model Error</h2>
        <div class="error-details">Sorry, an error has occurred!</div>
        <div class="error-actions">
          <a class="btn btn-primary btn-lg" href="/"><span class="glyphicon glyphicon-home"></span> Take Me Home </a>
          <a class="btn btn-default btn-lg" href="/Home/ContactUs"><span class="glyphicon glyphicon-envelope"></span> Contact Support </a>
        </div>
      </div>
    </div>
  </div>
</div>

0
2018-06-20 14:33