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

异步拦截器抛出异常未被ExceptionMiddleware捕获问题排查

问题:Castle动态代理异步拦截器抛出的异常无法被ExceptionMiddleware捕获

我有一个继承自MethodInterception类的切面类,在该类中抛出异常时,ExceptionMiddleware的HandleExceptionAsync方法未触发。我使用Castle.DynamicProxy实现方法拦截,由于它在异步方法中存在运行问题,因此通过Castle.Core.AsyncInterceptor扩展解决,但现在异步拦截器抛出的异常无法被异常中间件处理。若不将OnBefore方法定义为async,异常中间件可正常工作,但我需要在OnBefore中执行await操作,因此必须使用async。


ExceptionMiddleware代码

public class ExceptionMiddleware
{
    private RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _next(httpContext);
        }
        catch (Exception e)
        {
            await HandleExceptionAsync(httpContext, e);
        }
    }

    private Task HandleExceptionAsync(HttpContext httpContext, Exception e)
    {
        httpContext.Response.ContentType = "application/json";
        httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        string message = "Internal Server Error";

        if (e.GetType() == typeof(UnauthorizedAccessException))
        {
            message = e.Message;
            httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        }

        return httpContext.Response.WriteAsync(new ErrorDetails
        {
            StatusCode = httpContext.Response.StatusCode,
            Message = message
        }.ToString());
    }
}

SecuredOperation代码

public class SecuredOperation : MethodInterception
{
    private string[] _roles;
    private IHttpContextAccessor _httpcontextAccessor;

    public SecuredOperation()
    {
        _httpcontextAccessor = ServiceTool.ServiceProvider.GetService<IHttpContextAccessor>();
    }
    public SecuredOperation(string roles)
    {
        _roles = roles.Split(',');
        _httpcontextAccessor = ServiceTool.ServiceProvider.GetService<IHttpContextAccessor>();
    }

    protected override async void OnBefore(IInvocation invocation)
    {
        if (_httpcontextAccessor.HttpContext.User.Identity.IsAuthenticated)
        {
            if (_roles != null)
            {
                // some await proccess
                 var result = await _fga.check();
                 if(result != null) return;

                 throw new MemberAccessException(AuthMessages.AuthorizationForbidden);
            }
            return;
        }

        throw new UnauthorizedAccessException(AuthMessages.AuthorizationDenied);
    }
}

MethodInterception代码

public abstract class MethodInterception : MethodInterceptionBaseAttribute
{
    protected virtual void OnBefore(IInvocation invocation)
    {

    }
    protected virtual void OnAfter(IInvocation invocation)
    {

    }
    protected virtual void OnException(IInvocation invocation, System.Exception e)
    {

    }
    protected virtual void OnSuccess(IInvocation invocation)
    {

    }


    public override void InterceptSynchronous(IInvocation invocation)
    {
        var isSuccess = true;
        OnBefore(invocation);
        
        try
        {
            invocation.Proceed();
        }
        catch (Exception e)
        {
            isSuccess = false;
            OnException(invocation, e);
            throw;
        }
        finally
        {
            if (isSuccess)
            {
                OnSuccess(invocation);
            }
        }
        OnAfter(invocation);
    }

    public override void InterceptAsynchronous(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
    }

    protected async Task InternalInterceptAsynchronous(IInvocation invocation)
    {
        var isSuccess = true;
        OnBefore(invocation);
        try
        {
            invocation.Proceed();
            var task = (Task)invocation.ReturnValue;
            await task;
        }
        catch (Exception e)
        {
            isSuccess = false;
            OnException(invocation, e);
            throw;
        }
        finally
        {
            if (isSuccess)
            {
                OnSuccess(invocation);
            }
        }
        OnAfter(invocation);
    }

    public override void InterceptAsynchronous<TResult>(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
    }

    protected async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
    {
        TResult result;
        var isSuccess = true;
        OnBefore(invocation);
        try
        {
            invocation.Proceed();
            var task = (Task<TResult>)invocation.ReturnValue;
            result = await task;
        }
        catch (Exception e)
        {
            isSuccess = false;
            OnException(invocation, e);
            throw;
        }
        finally
        {
            if (isSuccess)
            {
                OnSuccess(invocation);
            }
        }
        OnAfter(invocation);
        return result;
    }
}

问题原因及解决方法

核心原因

你把OnBefore定义成了async void,这是异步编程的典型误区。async void方法抛出的异常无法被上层的try/catch捕获——因为这类方法不会返回可等待的Task,异常会直接抛到调用线程的同步上下文,完全脱离了你的拦截逻辑和异常中间件的捕获范围。

你的InternalInterceptAsynchronous方法只是调用了OnBefore(invocation),但没法等待async void方法执行完成,里面抛出的异常直接逃逸,根本没进入InternalInterceptAsynchronous的try/catch块,自然也到不了ExceptionMiddleware的捕获逻辑里。

解决步骤

1. 修改MethodInterception中的OnBefore签名

把返回类型从void改成Task,让它支持异步等待:

protected virtual Task OnBefore(IInvocation invocation)
{
    return Task.CompletedTask;
}

2. 更新拦截方法中的OnBefore调用

在所有异步拦截方法中await OnBefore(invocation),同步方法可以用同步等待的方式处理:

  • 异步无返回值拦截方法:
protected async Task InternalInterceptAsynchronous(IInvocation invocation)
{
    var isSuccess = true;
    await OnBefore(invocation); // 改为await等待异步操作完成
    try
    {
        invocation.Proceed();
        var task = (Task)invocation.ReturnValue;
        await task;
    }
    catch (Exception e)
    {
        isSuccess = false;
        OnException(invocation, e);
        throw;
    }
    finally
    {
        if (isSuccess)
        {
            OnSuccess(invocation);
        }
    }
    OnAfter(invocation);
}
  • 异步有返回值拦截方法同理:
protected async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
{
    TResult result;
    var isSuccess = true;
    await OnBefore(invocation); // 改为await等待异步操作完成
    try
    {
        invocation.Proceed();
        var task = (Task<TResult>)invocation.ReturnValue;
        result = await task;
    }
    catch (Exception e)
    {
        isSuccess = false;
        OnException(invocation, e);
        throw;
    }
    finally
    {
        if (isSuccess)
        {
            OnSuccess(invocation);
        }
    }
    OnAfter(invocation);
    return result;
}
  • 同步拦截方法(如需支持异步OnBefore):
public override void InterceptSynchronous(IInvocation invocation)
{
    var isSuccess = true;
    OnBefore(invocation).GetAwaiter().GetResult(); // 同步等待异步操作完成
    try
    {
        invocation.Proceed();
    }
    catch (Exception e)
    {
        isSuccess = false;
        OnException(invocation, e);
        throw;
    }
    finally
    {
        if (isSuccess)
        {
            OnSuccess(invocation);
        }
    }
    OnAfter(invocation);
}

3. 修改SecuredOperation中的OnBefore实现

把返回类型改成Task,保留原有逻辑:

protected override async Task OnBefore(IInvocation invocation)
{
    if (_httpcontextAccessor.HttpContext.User.Identity.IsAuthenticated)
    {
        if (_roles != null)
        {
            // some await proccess
             var result = await _fga.check();
             if(result != null) return;

             throw new MemberAccessException(AuthMessages.AuthorizationForbidden);
        }
        return;
    }

    throw new UnauthorizedAccessException(AuthMessages.AuthorizationDenied);
}

这样修改后,OnBefore中的异常会被InternalInterceptAsynchronous的try/catch捕获,重新抛出后就能被ExceptionMiddleware的捕获逻辑处理,触发HandleExceptionAsync方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:05:55