WebApi v2 ExceptionHandler未调用

Ben*_* E. 23 c# asp.net-web-api

为什么ExceptionHandler永远不会调用自定义,而是返回标准响应(不是我想要的)?

像这样注册

config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
Run Code Online (Sandbox Code Playgroud)

并实现这样的

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

并抛出这样(测试)

throw new NullReferenceException("testerror");
Run Code Online (Sandbox Code Playgroud)

在控制器或存储库中.

UPDATE

我没有另一个ExceptionFilter.

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

给定URL

GET http://localhost:XXXXX/template/lock/someId
Run Code Online (Sandbox Code Playgroud)

发送这个标题,我的ExceptionHandler作品

Host: localhost:XXXXX
Run Code Online (Sandbox Code Playgroud)

发送此标头,它不起作用,内置处理程序返回错误

Host: localhost:XXXXX
Origin: http://localhost:YYYY
Run Code Online (Sandbox Code Playgroud)

这可能是CORS请求的问题(我使用带有通配符的全局WebAPI CORS包)或最终使用我的ELMAH记录器.它也发生在Azure(网站)上托管,尽管内置的错误处理程序是不同的.

知道如何解决这个问题吗?

Ben*_* E. 30

原来,默认只处理最外层的异常,而不是存储库类中的异常.所以下面也必须被覆盖:

public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
    return context.ExceptionContext.IsOutermostCatchBlock;
}
Run Code Online (Sandbox Code Playgroud)

更新1

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

public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
    return true;
}
Run Code Online (Sandbox Code Playgroud)

更新2

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

  • 我刚刚找到了使用CORS进行异常处理的解决方案,看看这对您是否有帮助:http://stackoverflow.com/questions/24189315/exceptions-in-asp-net-web-api-custom-exception-handler-永远不会达到顶级-whe/24634485#24634485似乎即使是ASP.NET团队也不再使用ShouldHandle,但他们没有在任何地方提及它,从而引起混乱.我建议你忘记ExceptionHandler基类并像ASP.NET团队那样实现你的解决方案:https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/ExceptionHandling/DefaultExceptionHandler.cs (6认同)
  • 另请注意:http://stackoverflow.com/questions/22038800/can-anyone-explain-the-work-flow-of-iexceptionhandler-with-sample-client-applica看来他们已经用IsTopLevel替换了IsOutermostCatchBlock (3认同)

小智 7

这里真正的罪魁祸首是在消息处理pipline中由EnableCors方法插入的CorsMessageHandler.catch块拦截任何异常并在它到达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;
}
Run Code Online (Sandbox Code Playgroud)