You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC Core中ErrorController的HttpStatusCodeHandler为何未被调用?

问题排查与解决方案

你的问题核心在于错误处理中间件的配置错误和路由匹配逻辑冲突,导致HttpStatusCodeHandler始终无法被触发,以下是具体的排查点和修复步骤:

1. 核心问题分析

  • 生产环境路径指向错误:非开发环境下UseExceptionHandler("/Home/Error")指向HomeController的错误方法,而非自定义的ErrorController,直接跳过了你的错误处理逻辑。
  • 中间件配置重叠:开发环境同时启用UseExceptionHandler("/Error")和UseStatusCodePagesWithReExecute("/Error/{0}"),UseExceptionHandler会优先捕获所有未处理异常并跳转到/Error(即ErrorHandler方法),覆盖了状态码路由的触发逻辑。
  • 路由特性冲突:Action上的[Route]特性与端点路由配置重复,可能导致路由匹配优先级混乱。

修复步骤

步骤1:统一错误处理中间件配置

修改Program.cs,让开发/生产环境都使用自定义ErrorController,并调整中间件顺序(错误处理需在路由之前):

var builder = WebApplication.CreateBuilder(args);

// 添加MVC服务
builder.Services.AddControllersWithViews();

var app = builder.Build();

// 静态文件中间件(需在错误处理前)
app.UseStaticFiles();

// 统一配置错误处理
app.UseExceptionHandler("/Error");
app.UseStatusCodePagesWithReExecute("/Error/{0}");

// 生产环境启用HSTS
if (!app.Environment.IsDevelopment())
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();

// 端点配置
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "error",
        pattern: "/Error/{statusCode}",
        defaults: new { controller = "Error", action = "HttpStatusCodeHandler" }
    );

    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

步骤2:移除Action上的路由特性

删除ErrorController中Action的[Route]特性,统一由端点路由管理:

public class ErrorController : Controller
{
    private readonly ILogger _logger;

    public ErrorController(ILogger<ErrorController> logger)
    {
        _logger = logger;
    }

    // 移除[Route("/Error/{statusCode}")]
    public IActionResult HttpStatusCodeHandler(int statusCode)
    {
        var statusCodeResult = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        switch (statusCode)
        {
            case 400:
                ViewBag.ErrorMessage = "Bad Request. The server could not understand the request.";
                _logger.LogWarning($"400 Error Occurred. Path = {statusCodeResult?.OriginalPath}" +
                                   $" and QueryString = {statusCodeResult?.OriginalQueryString}");
                break;

            case 401:
                ViewBag.ErrorMessage = "Unauthorized. You are not authorized to access the requested resource.";
                _logger.LogWarning($"401 Error Occurred. Path = {statusCodeResult?.OriginalPath}" +
                                   $" and QueryString = {statusCodeResult?.OriginalQueryString}");
                break;

            case 403:
                ViewBag.ErrorMessage = "Forbidden. You do not have permission to access the requested resource.";
                _logger.LogWarning($"403 Error Occurred. Path = {statusCodeResult?.OriginalPath}" +
                                   $" and QueryString = {statusCodeResult?.OriginalQueryString}");
                break;

            case 404:
                ViewBag.ErrorMessage = "Not Found. The resource you requested could not be found.";
                _logger.LogWarning($"404 Error Occurred. Path = {statusCodeResult?.OriginalPath}" +
                                   $" and QueryString = {statusCodeResult?.OriginalQueryString}");
                break;

            case 500:
                ViewBag.ErrorMessage = "Internal Server Error. An unexpected error occurred on the server.";
                _logger.LogError($"500 Error Occurred. Path = {statusCodeResult?.OriginalPath}" +
                                 $" and QueryString = {statusCodeResult?.OriginalQueryString}");
                break;

            default:
                ViewBag.ErrorMessage = "An error occurred while processing your request.";
                _logger.LogError($"Error {statusCode} occurred. Path = {statusCodeResult?.OriginalPath}" +
                                 $" and QueryString = {statusCodeResult?.OriginalQueryString}");
                break;
        }

        return View("Error");
    }

    // 移除[Route("/Error")]
    public IActionResult ErrorHandler()
    {
        var statusCodeResult = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        ViewBag.ErrorMessage = "Sorry, an error occurred.";
        _logger.LogError($"Error occurred. Path = {statusCodeResult?.OriginalPath}" +
                                 $" and QueryString = {statusCodeResult?.OriginalQueryString}");

        return View("Error");
    }
}

步骤3:让异常类错误进入状态码处理器(可选)

默认UseExceptionHandler会直接跳转到/Error,如果希望500等异常也通过HttpStatusCodeHandler处理,可自定义异常处理逻辑:

app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        // 重定向到带状态码的错误路由
        await context.Response.Redirect($"/Error/{context.Response.StatusCode}");
    });
});

验证测试

  • 访问不存在的路径(如/NotFound),应触发404并进入HttpStatusCodeHandler
  • 故意抛出异常(如在HomeController的Action中写throw new Exception();),应触发500并进入对应处理分支

内容的提问来源于stack exchange,提问作者Goober

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 10:35:05