ASP.NET Core Web API自定义中间件处理500错误并返回TResponse
问题描述
我在ASP.NET Core Web API中实现了自定义中间件ResponseMiddleware,用于处理并向客户端发送响应,正常响应由控制器Action返回。当前遇到的问题是:当控制器GetT方法执行抛出500内部服务器错误时,需要捕获异常并识别类型,保持返回500状态码的同时,返回包含自定义错误信息的TResponse格式响应(该类包含成功状态标识和错误消息字段),而非默认错误响应。
现有控制器Action及中间件代码如下:
public async Task<ActionResult<TResponse>> GetT(TRequest request) { // ...业务逻辑 TResponse response = await _tService.GetT(request); // ...其他逻辑 return Ok(response); } public class ResponseMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var originalBodyStream = context.Response.Body; try { using var memoryStream = new MemoryStream(); context.Response.Body = memoryStream; await next(context); memoryStream.Position = 0; var reader = new StreamReader(memoryStream); var responseBody = await reader.ReadToEndAsync(); memoryStream.Position = 0; await memoryStream.CopyToAsync(originalBodyStream); var requestTelemetry = context.Features.Get<RequestTelemetry>(); requestTelemetry?.Properties.Add("ResponseBody", responseBody); Log.Information(responseBody); } catch (Exception ex) { string exType = ex.GetType().ToString(); context.Response.StatusCode = 500; if (exType=="sql") //Data query error { context.Response.ContentType = "application/json"; TResponse response = new TResponse(); // 此处不知道如何将response转换为响应体输出 } finally { context.Response.Body = originalBodyStream; } } }
我卡在了中间件的catch块:不知道如何将设置好错误信息的TResponse对象转换为响应体,像正常流程那样通过内存流处理并正确返回。
解决方案
要在catch块中返回格式统一的TResponse,可复用正常流程的内存流处理逻辑,结合框架默认JSON序列化配置实现,具体步骤及修改后代码如下:
修改后完整中间件代码
using System.Text.Json; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; public class ResponseMiddleware : IMiddleware { // 注入框架默认JSON序列化选项,保证响应格式统一 private readonly JsonSerializerOptions _jsonOptions; public ResponseMiddleware(IOptions<JsonOptions> jsonOptions) { _jsonOptions = jsonOptions.Value.JsonSerializerOptions; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var originalBodyStream = context.Response.Body; try { using var memoryStream = new MemoryStream(); context.Response.Body = memoryStream; await next(context); memoryStream.Position = 0; var reader = new StreamReader(memoryStream); var responseBody = await reader.ReadToEndAsync(); memoryStream.Position = 0; await memoryStream.CopyToAsync(originalBodyStream); var requestTelemetry = context.Features.Get<RequestTelemetry>(); requestTelemetry?.Properties.Add("ResponseBody", responseBody); Log.Information(responseBody); } catch (Exception ex) { // 设置状态码和响应类型 context.Response.StatusCode = StatusCodes.Status500InternalServerError; context.Response.ContentType = "application/json"; // 构建错误响应对象 var errorResponse = new TResponse { IsSuccess = false, ErrorMessage = "服务器内部错误,请稍后重试" // 按需设置其他字段 }; // 精准识别SQL异常(替换原字符串判断,类型更安全) if (ex is SqlException) { errorResponse.ErrorMessage = "数据查询出错,请稍后重试"; } // 复用内存流逻辑序列化响应 using var memoryStream = new MemoryStream(); await JsonSerializer.SerializeAsync(memoryStream, errorResponse, _jsonOptions); memoryStream.Position = 0; await memoryStream.CopyToAsync(originalBodyStream); // 记录错误日志及响应内容 var errorResponseBody = JsonSerializer.Serialize(errorResponse, _jsonOptions); var requestTelemetry = context.Features.Get<RequestTelemetry>(); requestTelemetry?.Properties.Add("ErrorResponseBody", errorResponseBody); Log.Error(ex, errorResponseBody); } finally { context.Response.Body = originalBodyStream; } } }
关键说明
- 统一序列化配置:通过注入
IOptions<JsonOptions>获取框架默认JSON序列化规则,确保错误响应与正常响应的JSON格式(如驼峰命名、日期格式)完全一致。 - 异常类型判断优化:用
is关键字直接判断具体异常类型(如SqlException),替代原字符串匹配方式,更可靠且类型安全。 - 复用内存流逻辑:与正常响应流程保持一致的内存流处理方式,避免流操作异常,保证响应写入逻辑统一。
- 完善日志记录:错误分支同时记录异常栈信息和响应内容,方便后续问题排查。
内容的提问来源于stack exchange,提问作者user21225864
相关产品推荐
相关产品推荐

