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

.NET 8全局异常处理器无法写入response.Body问题求助

全局异常处理器无法返回响应体问题

问题背景

我正在开发一组API,需要通过全局异常处理器统一处理未捕获异常,要求完成格式化并记录异常、返回标准化响应体两项功能。目前日志记录功能已实现,但无论用哪种方式将对象写入response.Body,通过Postman或其他工具调用API时都无法获取到响应体。

我能修改response.StatusCode并得到对应的状态码,但响应体始终为空。已经调整过中间件/异常处理器的调用顺序,排查后续中间件是否修改响应,结果一致;也尝试过Kestrel、IIS、Docker等不同宿主,问题依旧。

已尝试的三种实现方案

1. 自定义异常中间件

最初采用自定义中间件实现,但响应体无法返回:

public class ExceptionMiddleware
{
    private RequestDelegate _next;
    private SecurityHeadersPolicy _policy;

    public ExceptionMiddleware(RequestDelegate next, SecurityHeadersPolicy policy)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context, ILogger logger)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            logger.LogError((int)CommonLogCode.UnhandledError, "An unhandled error occurred.", ex);
            context.Request.Headers.TryGetValue(HeaderConstants.TransactionIdKey, out var transactionId);


            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;

            var response = new DeveloperMessageResponse()
            {
                ErrorCode = string.Format("{0:0000}", CommonLogCode.UnhandledError),
                ReferenceId = transactionId,
                DeveloperMessage = "An unhandled exception occurred.",
                DisplayMessage = "An unexpected error occurred."
            };

            IHeaderDictionary headers = context.Response.Headers;

            foreach (var headerValuePair in _policy.SetHeaders)
            {
                headers[headerValuePair.Key] = headerValuePair.Value;
            }

            foreach (var header in _policy.RemoveHeaders)
            {
                headers.Remove(header);
            }

            await context.Response.WriteAsync(JsonSerializer.Serialize(response));

        }
    }

}

2. 自定义IExceptionHandler类

查阅资料得知.NET 8推荐实现IExceptionHandler,但问题依旧:

public class GlobalExceptionHandler : IExceptionHandler
{
    private readonly ILogger _logger;

    public GlobalExceptionHandler(ILogger logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        _logger.LogError((int)CommonLogCode.UnhandledError, "An unhandled error occurred.", exception);
        
        httpContext.Request.Headers.TryGetValue(HeaderConstants.TransactionIdKey, out var transactionId);

        httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        httpContext.Response.ContentType = "application/json";
        IHeaderDictionary headers = httpContext.Response.Headers;

        var response = new DeveloperMessageResponse()
        {
            ErrorCode = string.Format("{0:0000}", (int)CommonLogCode.UnhandledError),
            ReferenceId = transactionId,
            DeveloperMessage = "An unhandled exception occurred.",
            DisplayMessage = "An unexpected error occurred."
        };

        await httpContext.Response.WriteAsJsonAsync(JsonSerializer.Serialize(response));
        
        return true;
    }
}

3. app.UseExceptionHandler Lambda表达式(当前实现)

尝试了WriteAsJsonAsync、WriteAsync、Body.Write等多种写入方法,仍无法返回响应体:

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async context =>
    {
        var logger = app.Services.GetRequiredService<ILogger>();
        var policy = securityPolicy.Build();
        var exceptionHandlerFeature =
           context.Features.Get<IExceptionHandlerFeature>();
        logger.LogError((int)CommonLogCode.UnhandledError, "An unhandled error occurred.", exceptionHandlerFeature?.Error);

        context.Request.Headers.TryGetValue(HeaderConstants.TransactionIdKey, out var transactionId);
        //context.Response.Clear();
        context.Response.StatusCode = (int)HttpStatusCode.NotFound;
        context.Response.ContentType = "application/json";
        IHeaderDictionary headers = context.Response.Headers;

        foreach (var headerValuePair in policy.SetHeaders)
        {
            headers[headerValuePair.Key] = headerValuePair.Value;
        }

        foreach (var header in policy.RemoveHeaders)
        {
            headers.Remove(header);
        }
        var response = new DeveloperMessageResponse()
        {
            ErrorCode = string.Format("{0:0000}", (int)CommonLogCode.UnhandledError),
            ReferenceId = transactionId,
            DeveloperMessage = "An unhandled exception occurred.",
            DisplayMessage = "An unexpected error occurred."
        };
        //var responseBuffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(response));
        //await context.Response.Body.WriteAsync(responseBuffer, 0, responseBuffer.Length);
         
        await context.Response.WriteAsJsonAsync(JsonSerializer.Serialize(response));
        //await context.Response.CompleteAsync();

    });
});

完整Program.cs代码

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(options => { options.AddServerHeader = false; options.AllowResponseHeaderCompression = false; });


// Add services to the container.
builder.Services.AddMvcCore().AddApiExplorer();
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
    });

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddHttpContextAccessor();
builder.Services.AddApiAndSwaggerVersioningconfigurations();
builder.Services.AddAutoMapperDependencies("UserAPI*.dll");
builder.Services.AddFrameworkDependencies();
builder.Services.AddApplicationDependencies("UserAPI*.dll");
//builder.Services.AddExceptionHandler<GlobalExceptionHandler>();


var app = builder.Build();
var securityPolicy = new SecurityHeadersBuilder().AddDefaultAPISecurePolicy();
//app.UseExceptionMiddleware(securityPolicy);

app.UseForwardedHeaders();
//app.UseExceptionHandler(opt => { });

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async context =>
    {
        var logger = app.Services.GetRequiredService<ILogger>();
        var policy = securityPolicy.Build();
        var exceptionHandlerFeature =
           context.Features.Get<IExceptionHandlerFeature>();
        logger.LogError((int)CommonLogCode.UnhandledError, "An unhandled error occurred.", exceptionHandlerFeature?.Error);

        context.Request.Headers.TryGetValue(HeaderConstants.TransactionIdKey, out var transactionId);
        //context.Response.Clear();
        context.Response.StatusCode = (int)HttpStatusCode.NotFound;
        context.Response.ContentType = "application/json";
        IHeaderDictionary headers = context.Response.Headers;

        foreach (var headerValuePair in policy.SetHeaders)
        {
            headers[headerValuePair.Key] = headerValuePair.Value;
        }

        foreach (var header in policy.RemoveHeaders)
        {
            headers.Remove(header);
        }
        var response = new DeveloperMessageResponse()
        {
            ErrorCode = string.Format("{0:0000}", (int)CommonLogCode.UnhandledError),
            ReferenceId = transactionId,
            DeveloperMessage = "An unhandled exception occurred.",
            DisplayMessage = "An unexpected error occurred."
        };
        //var responseBuffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(response));
        //await context.Response.Body.WriteAsync(responseBuffer, 0, responseBuffer.Length);
         
        await context.Response.WriteAsJsonAsync(JsonSerializer.Serialize(response));
        //await context.Response.CompleteAsync();

    });
});
app.UseResponseSecurityHeaderMiddleware(securityPolicy);

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseHsts();
}

app.UseHttpMiddleware();
app.UsePerformanceTrackingMiddleware();

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();

int suppressVersionsBelow = 0;
int.TryParse(builder.Configuration[ConfigConstants.SuppressVersionsBelow], out suppressVersionsBelow);
var versioningProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
app.ConfigureSwaggerUI(versioningProvider, suppressVersionsBelow);

app.MapControllers();

app.Run();

期望结果

希望返回以下格式的JSON响应体:

{
    "ErrorCode": "0000",
    "ReferenceId": "请求中的TransactionId",
    "DeveloperMessage": "An unhandled exception occurred.",
    "DisplayMessage": "An unexpected error occurred."
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 02:24:55