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逻辑),可以自定义全局中间件实现:
- 编写中间件类:
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); } }
- 在
Program.cs中注册中间件,注意顺序必须放在鉴权、路由中间件之前:
// 放在所有中间件的最靠前位置 app.UseMiddleware<CustomErrorHandleMiddleware>(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers();
内容的提问来源于stack exchange,提问作者nour
相关产品推荐
相关产品推荐

