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

.NET Core 7全局异常捕获失效问题求助

.NET Core 7 全局异常捕获失效问题排查与解决

问题背景

开发.NET Core 7应用时,希望捕获所有未处理异常并返回包含特定格式的JSON 500响应。尝试两种方案均未生效:

  • 自定义异常中间件的catch块从未触发,返回带堆栈跟踪的text/plain原始错误
  • UseExceptionHandler的回调也从未执行

初始代码细节

自定义中间件Invoke方法

public virtual async Task Invoke(HttpContext context)
{
    try
    {
        await this.RequestDelegate.Invoke(context);
    }
    catch (Exception ex)
    {
        // 检查响应是否已开始发送
        if (!context.Response.HasStarted)
        {
            // 创建异常响应模型
            var model = new UnhandledExceptionModel(ex);
            var response = new ObjectResult(model)
                    {
                        StatusCode = StatusCodes.Status500InternalServerError
                    };

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

            await context.Response.WriteAsJsonAsync(response).ConfigureAwait(false);
        }
        else
            throw;
    }
}

尝试的UseExceptionHandler配置

app.UseExceptionHandler(a => a.Run(async context =>
{
    var error = context.Features.Get<IExceptionHandlerFeature>().Error;

    var model = new UnhandledExceptionModel(error);
    context.Response.ContentType = "application/json";
    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

    await context.Response.WriteAsJsonAsync(model);
}));

更新1:完整中间件与Startup配置

自定义中间件结构

public class ExceptionHandlingMiddleware : BaseMiddleware
{
    public ExceptionHandlingMiddleware(RequestDelegate next)
        : base(next)
    {
    }
}

/// <summary>
/// 中间件实现的基类
/// </summary>
public abstract class BaseMiddleware : IMiddleware
{
    /// <summary>
    /// 请求处理委托
    /// </summary>
    public RequestDelegate RequestDelegate { get; }

    /// <summary>
    /// 使用指定的请求委托初始化中间件实例
    /// </summary>
    /// <param name="next"></param>
    public BaseMiddleware(RequestDelegate next)
    {
        this.RequestDelegate = next;
    }

    /// <summary>
    /// 中间件的请求处理方法
    /// </summary>
    /// <param name="context">请求上下文</param>
    /// <returns>任务</returns>
    public virtual async Task Invoke(HttpContext context)
    {
        try
        {
            await Task.Run(() => this.RequestDelegate.Invoke(context));
        }
        catch (Exception ex)
        {
            // 检查响应是否已开始发送
            if (!context.Response.HasStarted)
            {
                // 创建异常响应模型
                var model = new UnhandledExceptionModel(ex);
                var response = new ObjectResult(model)
                {
                    StatusCode = StatusCodes.Status500InternalServerError
                };

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

                await context.Response.WriteAsJsonAsync(response).ConfigureAwait(false);
            }
            else
                throw;
        }
    }
}

/// <summary>
/// 中间件核心接口定义
/// </summary>
public interface IMiddleware
{
    /// <summary>
    /// 请求处理委托
    /// </summary>
    RequestDelegate RequestDelegate { get; }

    /// <summary>
    /// 中间件请求处理方法
    /// </summary>
    /// <param name="context">请求上下文</param>
    /// <returns>任务</returns>
    Task Invoke(HttpContext context);
}

Startup.cs的Configure方法

// 由运行时调用,用于配置HTTP请求管道
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 允许任意来源跨域
    app.UseCors("AllowAnyOrigin");

    app.UseMiddleware<ExceptionHandlingMiddleware>();

    // 配置异常处理器,确保异常返回统一格式模型(从未触发)
    app.UseExceptionHandler(a => a.Run(async context =>
    {
        var error = context.Features.Get<IExceptionHandlerFeature>().Error;

        var model = new UnhandledExceptionModel(error);
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        await context.Response.WriteAsJsonAsync(model);
    }));

    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "API Docs");
        c.EnableTryItOutByDefault();
    });

    app.UseSession()
       .UseHttpsRedirection()
       .UseRouting()
       .UseAuthorization()
       .UseEndpoints(endpoints =>
       {
           endpoints.MapControllers();
       });

    // 注入HttpContext供后端跟踪调用方IP等信息
    var httpContext = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    Core.HttpContext.Configure(httpContext);
}

更新2:测试接口验证

直接在接口中抛出异常,仍返回原始堆栈跟踪;注释app.UseDeveloperExceptionPage()后仅返回500状态码,无JSON响应:

[HttpGet("{id}")]
public IActionResult GetById(long id)
{
    throw new ApplicationException();
}

问题根源与解决方案

1. 自定义中间件的致命错误

BaseMiddleware中使用await Task.Run(() => this.RequestDelegate.Invoke(context))是核心问题:

  • RequestDelegate.Invoke(context)本身返回Task,用Task.Run包裹会导致异步操作脱离当前上下文,异常无法被外层catch捕获
  • 正确写法应为直接await this.RequestDelegate(context)(RequestDelegate是委托,直接调用即可,无需.Invoke)

修正后的BaseMiddleware Invoke方法:

public virtual async Task Invoke(HttpContext context)
{
    try
    {
        // 直接await委托调用,不要用Task.Run包裹
        await this.RequestDelegate(context);
    }
    catch (Exception ex)
    {
        if (!context.Response.HasStarted)
        {
            var model = new UnhandledExceptionModel(ex);
            
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // 无需包装为ObjectResult,直接序列化模型即可
            await context.Response.WriteAsJsonAsync(model).ConfigureAwait(false);
        }
        else
        {
            // 响应已开始,无法修改,重新抛出异常
            throw;
        }
    }
}

2. 中间件顺序问题

  • UseDeveloperExceptionPage会优先捕获所有异常并返回详细页面,导致UseExceptionHandler和自定义中间件失效,必须将异常处理相关中间件放在UseDeveloperExceptionPage之前
  • 同时,UseExceptionHandler和自定义中间件只需保留一个,避免冲突

修正后的Startup Configure方法顺序:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 跨域中间件放在最前
    app.UseCors("AllowAnyOrigin");

    // 异常处理中间件必须在DeveloperExceptionPage之前
    if (env.IsDevelopment())
    {
        app.UseMiddleware<ExceptionHandlingMiddleware>();
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseMiddleware<ExceptionHandlingMiddleware>();
    }

    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "API Docs");
        c.EnableTryItOutByDefault();
    });

    app.UseHttpsRedirection()
       .UseRouting()
       .UseSession()
       .UseAuthorization()
       .UseEndpoints(endpoints =>
       {
           endpoints.MapControllers();
       });

    var httpContext = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    Core.HttpContext.Configure(httpContext);
}

3. 自定义IMiddleware接口冲突

.NET Core自带Microsoft.AspNetCore.Http.IMiddleware接口,自定义同名接口会导致框架无法正确识别中间件逻辑,推荐改用传统中间件模式(无需实现接口):

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public ExceptionHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            if (!context.Response.HasStarted)
            {
                var model = new UnhandledExceptionModel(ex);
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                await context.Response.WriteAsJsonAsync(model);
            }
            throw;
        }
    }
}

// Startup中注册方式不变:app.UseMiddleware<ExceptionHandlingMiddleware>();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 23:04:56