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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:01:51