WebApi v2异常处理程序未被调用。

28

为什么自定义的ExceptionHandler从未被调用,而是返回了标准响应(不是我想要的那个)?

像这样注册:

config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

并实现为这样

public class GlobalExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        context.Result = new ExceptionResponse
        {
            statusCode = context.Exception is SecurityException ? HttpStatusCode.Unauthorized : HttpStatusCode.InternalServerError,
            message = "An internal exception occurred. We'll take care of it.",
            request = context.Request
        };
    }
}

public class ExceptionResponse : IHttpActionResult
{
    public HttpStatusCode statusCode { get; set; }
    public string message { get; set; }
    public HttpRequestMessage request { get; set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(statusCode);
        response.RequestMessage = request;
        response.Content = new StringContent(message);
        return Task.FromResult(response);
    }
}

这样扔就可以了(测试)

throw new NullReferenceException("testerror");

在控制器或存储库中。

更新

我没有另一个ExceptionFilter

我发现了这种行为的触发器:

给定的URL

GET http://localhost:XXXXX/template/lock/someId

通过发送这个头信息,我的 ExceptionHandler 就会工作了。

Host: localhost:XXXXX
发送这个头信息时,它无效,而内置的处理程序会返回错误。
Host: localhost:XXXXX
Origin: http://localhost:YYYY
这可能是由于CORS请求问题(我全局使用WebAPI CORS包和通配符)或我的ELMAH日志记录器。即使在Azure上托管(网站),也会发生这种情况,尽管内置的错误处理程序不同。
有任何解决方法吗?

你是否也有异常过滤器?另外,你能分享一下你的控制器或存储库代码是什么样子的吗?我们想确保你没有在某个地方捕获它并将其转换为HttpResponseException或其他情况,在这种情况下,异常处理程序将不会被调用。 - Kiran
@KiranChalla:上面的更新很有趣,谢谢! - Benjamin E.
2个回答

37

事实证明,缺省情况下只处理最外层的异常,而不是存储库类中的异常。因此,下面的内容也必须被覆盖:

public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
    return context.ExceptionContext.IsOutermostCatchBlock;
}

更新1

WebAPI v2不再使用IsOutermostCatchBlock。 无论如何,我的实现没有任何变化,因为ShouldHandle中的新代码仍然阻止了我的错误处理程序。 所以我仍在使用这个,我的错误处理程序被调用一次。 我通过这种方式捕获控制器和存储库中的错误。

public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
    return true;
}

更新2

由于这个问题引起了如此多的关注,请注意,当前的解决方案是下面评论中由@JustAMartin提供的链接


3
请注意这个链接:https://dev59.com/A2Eh5IYBdhLWcg3ww15p。看起来他们已经用IsTopLevel替换了IsOutermostCatchBlock。 - JustAMartin
感谢更新,@Martin。仍然是一样的,不知道为什么控制器错误不能使用标准实现处理。 - Benjamin E.
7
我刚刚找到了CORS异常处理的解决方案,看看它是否对你有帮助:https://dev59.com/ImAf5IYBdhLWcg3w3l6l#24634485 。即使是ASP.NET团队也不再使用ShouldHandle,但他们没有在任何地方提到过这一点,因此引起了困惑。我建议你忘记ExceptionHandler基类并像ASP.NET团队一样实现你的解决方案:https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/ExceptionHandling/DefaultExceptionHandler.cs。 - JustAMartin
非常感谢!这对我有用,唯一的区别是我使用了public override而不是public virtual。 - ajpetersen
public override bool ShouldHandle(ExceptionHandlerContext context) => true; 这就可以了。谢谢! - FelipeDrumond

8

真正的罪犯是由EnableCors方法插入消息处理流程中的CorsMessageHandler。捕获块拦截任何异常并将其转换为响应,以防止其到达HTTPServer try-catch块和ExceptionHandler逻辑被调用。

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    CorsRequestContext corsRequestContext = request.GetCorsRequestContext();
    HttpResponseMessage result;
    if (corsRequestContext != null)
    {
    try
    {
        if (corsRequestContext.IsPreflight)
        {
        result = await this.HandleCorsPreflightRequestAsync(request, corsRequestContext, cancellationToken);
        return result;
        }
        result = await this.HandleCorsRequestAsync(request, corsRequestContext, cancellationToken);
        return result;
    }
    catch (Exception exception)
    {
        result = CorsMessageHandler.HandleException(request, exception);
        return result;
    }
    }
    result = await this.<>n__FabricatedMethod3(request, cancellationToken);
    return result;
}

1
可以在https://dev59.com/ImAf5IYBdhLWcg3w3l6l#24634485找到解决方案。 - 0xced

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接