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

ASP.NET Core 6 MVC错误处理:策略与最佳实践咨询

ASP.NET Core 错误处理常见问题解答

1. 是否需要捕获所有错误类型并返回特定视图?

不是必须为每一种状态码都单独返回特定视图,核心看应用场景:

  • 面向普通用户的MVC/Web应用:为404(资源找不到)、500(服务器错误)、403(权限不足)这类用户感知强的高频错误提供自定义视图,能提升体验;但像400(请求格式错误)这类,表单场景可单独处理,API场景直接返回标准化JSON即可。
  • 后端API:更建议返回统一格式的错误JSON(包含错误码、消息、详情),而非视图,方便前端统一处理展示。
    原则:只处理对用户/业务有价值的错误类型,避免过度设计。

2. 最佳实践:Middleware、Filters还是其他方式?

全局错误处理:优先用 Middleware

Middleware 能捕获整个请求管道的错误(包括路由匹配失败、静态资源不存在等),是全局错误处理的最优选择。

自定义错误中间件示例

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ErrorHandlingMiddleware> _logger;

    public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);

            // 处理非异常抛出的状态码错误(如404)
            if (context.Response.StatusCode >= 400)
            {
                await HandleStatusCodeErrorAsync(context);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "未处理的全局异常");
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleStatusCodeErrorAsync(HttpContext context)
    {
        context.Response.ContentType = "text/html";
        var statusCode = context.Response.StatusCode;
        var viewName = statusCode switch
        {
            404 => "NotFound",
            403 => "Forbidden",
            500 => "ServerError",
            _ => "GenericError"
        };

        var result = new ViewResult { ViewName = viewName };
        var executor = context.RequestServices.GetRequiredService<IActionResultExecutor<ViewResult>>();
        await executor.ExecuteAsync(new ActionContext(context, context.GetRouteData(), new ActionDescriptor()), result);
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        context.Response.ContentType = "text/html";
        
        var result = new ViewResult { ViewName = "ServerError" };
        var executor = context.RequestServices.GetRequiredService<IActionResultExecutor<ViewResult>>();
        await executor.ExecuteAsync(new ActionContext(context, context.GetRouteData(), new ActionDescriptor()), result);
    }
}

// 扩展方法注册中间件
public static class ErrorHandlingMiddlewareExtensions
{
    public static IApplicationBuilder UseCustomErrorHandling(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ErrorHandlingMiddleware>();
    }
}

// Program.cs中使用(放在路由、静态文件中间件之后,端点中间件之前)
app.UseCustomErrorHandling();

控制器/动作级别错误处理:用 Filters

如果只需针对特定控制器或动作处理错误,异常过滤器更精准,能控制范围。

异常过滤器示例

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly ILogger<CustomExceptionFilter> _logger;

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

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception, "控制器内异常");
        context.Result = new ViewResult
        {
            ViewName = "ServerError",
            ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), context.ModelState)
            {
                {"ErrorMessage", "请求处理失败,请稍后重试"}
            }
        };
        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
    }
}

// 注册方式1:全局注册
builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<CustomExceptionFilter>();
});

// 注册方式2:控制器级别注册
[TypeFilter(typeof(CustomExceptionFilter))]
public class HomeController : Controller
{
    // 控制器逻辑
}

补充:内置状态码页面中间件

ASP.NET Core 内置的 UseStatusCodePagesWithReExecute 可快速实现状态码到视图的映射,无需自定义中间件:

// Program.cs
app.UseStatusCodePagesWithReExecute("/Error/{0}");

// 新增Error控制器
public class ErrorController : Controller
{
    public IActionResult Index(int statusCode)
    {
        var viewName = statusCode switch
        {
            404 => "NotFound",
            403 => "Forbidden",
            500 => "ServerError",
            _ => "GenericError"
        };
        return View(viewName);
    }
}

3. 若不需要为每种错误返回特定视图,错误处理应遵循什么路径?

遵循极简+集中化原则:

  • 区分环境:开发环境用 UseDeveloperExceptionPage 展示详细错误信息方便调试;生产环境用统一错误响应。
  • 统一错误输出:所有未处理错误返回同一个友好视图(MVC)或标准化JSON(API),避免用户看到混乱信息。
  • 集中日志:所有错误必须记录到日志系统(如Serilog、NLog),包含错误详情、请求上下文,方便排查问题。
  • 避免重复处理:不要同时混用多种错误处理方式(如同时加中间件和过滤器),防止逻辑冲突。

生产环境示例配置:

// Program.cs
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    // 处理异常错误
    app.UseExceptionHandler("/Error");
    // 处理非异常的状态码错误
    app.UseStatusCodePagesWithRedirects("/Error");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:35:28