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

ASP.NET Core Web API如何用try catch捕获401、403错误并封装统一返回格式

实现说明

首先明确:你在Controller的Action中写的try/catch无法捕获到401、403错误。JWT鉴权的校验逻辑是ASP.NET Core中间件管道的前置逻辑,执行顺序早于Controller Action调用,鉴权失败会直接返回响应,不会进入Action的代码块,所以无法通过Action层的try/catch处理这两类状态码。

推荐以下两种标准实现方案:

方案1:使用JWT鉴权内置事件处理(最轻量,仅处理鉴权相关错误)

直接在JWT服务配置的位置添加事件回调,处理鉴权阶段产生的401、403响应,代码如下:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        // 保留你原有JWT校验配置不变
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };

        // 新增鉴权事件处理
        options.Events = new JwtBearerEvents
        {
            // 处理401未授权(令牌过期、签名错误等场景)
            OnChallenge = context =>
            {
                // 跳过默认的401响应逻辑
                context.HandleResponse();
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                var errorRes = new ErrorMessage
                {
                    httpStatus = StatusCodes.Status401Unauthorized,
                    Message = "令牌无效或已过期,请重新登录"
                };
                return context.Response.WriteAsJsonAsync(errorRes);
            },
            // 处理403权限不足(角色/声明校验失败场景)
            OnForbidden = context =>
            {
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                var errorRes = new ErrorMessage
                {
                    httpStatus = StatusCodes.Status403Forbidden,
                    Message = "当前账号无该接口访问权限"
                };
                return context.Response.WriteAsJsonAsync(errorRes);
            }
        };
    });

方案2:自定义全局中间件(更通用,统一处理所有状态码+全局异常)

如果需要统一处理所有HTTP状态码、以及全局异常(可以省掉每个Action里重复的try/catch逻辑),可以自定义全局中间件实现:

  1. 编写中间件类:
public class CustomErrorHandleMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<CustomErrorHandleMiddleware> _logger;

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

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
            // 捕获管道执行后返回的非成功状态码
            if (context.Response.StatusCode is StatusCodes.Status401Unauthorized or StatusCodes.Status403Forbidden)
            {
                await WrapErrorResponse(context);
            }
        }
        catch (Exception ex)
        {
            // 全局捕获所有未处理的业务异常,无需每个Action写try/catch
            _logger.LogError(ex, "接口请求发生未处理异常");
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            await context.Response.WriteAsJsonAsync(new ErrorMessage
            {
                httpStatus = StatusCodes.Status500InternalServerError,
                Message = "服务器内部错误"
            });
        }
    }

    private async Task WrapErrorResponse(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        var errorRes = context.Response.StatusCode switch
        {
            StatusCodes.Status401Unauthorized => new ErrorMessage
            {
                httpStatus = StatusCodes.Status401Unauthorized,
                Message = "令牌无效或已过期,请重新登录"
            },
            StatusCodes.Status403Forbidden => new ErrorMessage
            {
                httpStatus = StatusCodes.Status403Forbidden,
                Message = "当前账号无该接口访问权限"
            },
            _ => new ErrorMessage
            {
                httpStatus = context.Response.StatusCode,
                Message = "请求处理失败"
            }
        };
        await context.Response.WriteAsJsonAsync(errorRes);
    }
}
  1. 在Program.cs中注册中间件,注意顺序必须放在鉴权、路由中间件之前:
// 放在所有中间件的最靠前位置
app.UseMiddleware<CustomErrorHandleMiddleware>();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 12:06:07