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

ASP.NET Core全局异常中间件如何捕获异常发生的类与方法?

解决方案:替代ASP.NET Core重复try-catch的异常记录方案

首先明确:仅靠全局中间件无法直接获取异常发生的具体类和方法信息。因为全局中间件处于请求管道的外层,异常抛出后已经脱离了业务代码的执行上下文,反射_next只能拿到管道内的中间件类型,无法定位到实际抛出异常的业务类或方法,且不解析堆栈的话没有其他直接途径获取该信息。

以下是几种可行的替代方案:

1. 控制器级异常过滤器(ActionFilterAttribute)

针对MVC/WebAPI控制器的场景,自定义异常过滤器可以直接获取当前执行的控制器和Action信息,无需解析堆栈:

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;

    public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
        if (actionDescriptor != null)
        {
            string className = actionDescriptor.ControllerTypeInfo.FullName;
            string methodName = actionDescriptor.MethodInfo.Name;

            _logger.LogError(context.Exception, "异常发生在类:{ClassName},方法:{MethodName}", className, methodName);
        }
        else
        {
            // 处理非控制器Action的异常(如Razor Pages),可 fallback 到堆栈记录
            _logger.LogError(context.Exception, "发生未明确定位的异常");
        }

        // 可选:标记异常已处理,返回自定义响应
        // context.ExceptionHandled = true;
        // context.Result = new ObjectResult("服务器内部错误") { StatusCode = StatusCodes.Status500InternalServerError };
    }
}

注册过滤器到控制器服务:

// Program.cs
builder.Services.AddControllers(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});

2. AOP拦截器(推荐用于非控制器的业务服务)

如果需要覆盖控制器之外的业务类方法(如Service层),可以用AOP框架(如AspectCore)实现全局异常拦截,直接获取目标类和方法信息:

public class ExceptionInterceptor : AbstractInterceptorAttribute
{
    private readonly ILogger<ExceptionInterceptor> _logger;

    public ExceptionInterceptor(ILogger<ExceptionInterceptor> logger)
    {
        _logger = logger;
    }

    public override async Task Invoke(AspectContext context, AspectDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            string className = context.TargetType.FullName;
            string methodName = context.MethodInfo.Name;

            _logger.LogError(ex, "异常发生在类:{ClassName},方法:{MethodName}", className, methodName);
            throw; // 继续抛出异常,或根据需求处理
        }
    }
}

配置AspectCore动态代理:

// Program.cs
builder.Services.ConfigureDynamicProxy(options =>
{
    // 全局拦截所有服务
    options.Interceptors.AddTyped<ExceptionInterceptor>();
    // 或仅拦截特定命名空间的类
    // options.Interceptors.AddTyped<ExceptionInterceptor>(predicate: type => type.Namespace.StartsWith("YourNamespace.Services"));
});

// 注意:如果是构造注入的服务,需要用CreateProxyOfService获取实例,或通过特性标记
// 比如在服务类上添加[ExceptionInterceptor]特性

3. 封装异常扩展方法(兼容全局中间件)

如果必须保留全局中间件,可以通过扩展方法简化原有try-catch的代码,将类和方法名存入异常的Data集合,中间件直接读取:

public static class ExceptionExtensions
{
    public static Exception WithSourceInfo(this Exception ex, string className, string methodName)
    {
        ex.Data["ClassName"] = className;
        ex.Data["MethodName"] = methodName;
        return ex;
    }
}

业务代码中使用:

public async Task DoSomething()
{
    try
    {
        // 业务逻辑
    }
    catch (Exception ex)
    {
        throw ex.WithSourceInfo(GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod()?.Name);
    }
}

全局中间件中读取:

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            string className = ex.Data["ClassName"]?.ToString() ?? "未知类";
            string methodName = ex.Data["MethodName"]?.ToString() ?? "未知方法";

            _logger.LogError(ex, "异常发生在类:{ClassName},方法:{MethodName}", className, methodName);
            // 处理响应
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        return context.Response.WriteAsync(JsonSerializer.Serialize(new { Message = "服务器内部错误" }));
    }
}

内容的提问来源于stack exchange,提问作者Carlos Jimenez Bermudez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 18:27:30