启用 CORS 时,ASP.NET Web API 自定义异常处理程序中的异常永远不会到达顶级 [英] Exceptions in ASP.NET Web API custom exception handler never reach top level when CORS is enabled

查看:14
本文介绍了启用 CORS 时,ASP.NET Web API 自定义异常处理程序中的异常永远不会到达顶级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个自定义的 Web API 全局异常处理程序,如下所示:

I have created a custom Web API global exception handler like this:

public class MyGlobalExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        // here I handle them all, no matter sync or not
    }

    public override Task HandleAsync(ExceptionHandlerContext context, 
        CancellationToken cancellationToken)
    {
        // not needed, but I left it to debug and find out why it never reaches Handle() method
        return base.HandleAsync(context, cancellationToken);
    }

    public override bool ShouldHandle(ExceptionHandlerContext context)
    {
        // not needed, but I left it to debug and find out why it never reaches Handle() method
        return context.CatchBlock.IsTopLevel;
    }
}

我正在 Global.asax Application_Start 中注册它:

I'm registering it in my Global.asax Application_Start:

GlobalConfiguration.Configuration.Services.Replace(typeof(IExceptionHandler),
    new MyGlobalExceptionHandler());

一切正常,无论我在哪里抛出异常 - 在控制器方法内部或在控制器方法上方的自定义属性中,无论我是从 AJAX 请求还是直接从浏览器调用它.

Everything worked fine, no matter where I threw exceptions - inside controller methods or in my custom attributes above controller methods, and no matter if I call it from AJAX requests or directly from browser.

但有一天,我的 AJAX 请求需要 CORS 支持.我按照文章 在 ASP.NET Web API 中启用跨域请求

But then one day I needed CORS support for my AJAX requests. I enabled CORS globally as described in the article Enabling Cross-Origin Requests in ASP.NET Web API

var cors = new EnableCorsAttribute("*", "*", "*"); // * is just for debugging purpose
config.EnableCors(cors);

起初一切正常,CORS 按预期工作,服务器响应 OPTIONS 请求.

At first everything seemed OK, CORS worked as expected, the server responded to OPTIONS request.

但是当我想检查我的身份验证时问题就开始了.突然异常被吞下,我得到了一个空的 JSON {} 响应,而不是我在 MyGlobalExceptionHandler 的 Handle() 方法中创建的自定义 JSON 格式的异常.

But the problems started when I wanted to check my authentication. Suddenly the exception was swallowed and I got an empty JSON {} response instead of my custom JSON formatted exception which I create in the Handle() method of my MyGlobalExceptionHandler.

在调试时,我惊讶地发现现在对于 AJAX 请求,ShouldHandle() 方法仅在 IsTopLevel = false 时被调用,因此异常永远不会冒泡,也永远不会到达我的 Handle() 方法.一旦我禁用 CORS,一切都会恢复正常(当然,跨域请求除外).

While debugging, I was surprised to find that now for AJAX requests ShouldHandle() method is called only with IsTopLevel = false, thus the exception never bubbles up and never reaches my Handle() method. As soon as I disable CORS, everything works fine again (except cross-domain requests, of course).

当我启用 CORS 时,为什么 IsTopLevel 永远不会为真?我应该如何解决这个问题?

还有一个副作用如下.如果 CORS 被禁用,那么如果我在 Handle() 方法中抛出任何异常,它会到达 Global.asax 中的 Application_Error 处理程序.但是如果我启用 CORS 并在我的处理程序方法中抛出异常,这些异常永远不会到达 Application_Error.

看来,我确切地发现了这种情况发生的时间.

It seems, I found exactly when this is happening.

如果我在启用 CORS 时在控制器方法中抛出异常,则 CORS 根本不会启动并且不会发送 Access-Control-Allow-Origin 标头.当浏览器没有收到标头时,它会立即中断请求,这种中断似乎也会影响异常处理程序——它永远不会到达 IsTopLevel = true 的 ShouldHandle() 方法.即使我在 IIS Express localhost 上运行从本地 html 文件到我的 websiet 的 AJAX 请求,Chrome 和 Mozilla 也会这样做.

If I throw an exception in a controller method when CORS is enabled, then CORS doesn't kick in at all and does not send the Access-Control-Allow-Origin header. When the browser doesn't receive the header, it immediately disrupts the request and this disruption seems to affect also the exception handler - it never reaches ShouldHandle() method with IsTopLevel = true. Chrome and Mozilla act like this even when I run AJAX request from a local html file to my websiet on IIS Express localhost.

但在 IE 11 上情况有所不同.当我在那里打开 html 文件时,它首先询问我是否允许启用脚本.在我同意之后,IE 11 会忽略不存在 CORS 标头的事实并且它不会中断请求,因此我的异常处理程序接收 IsTopLevel = true 并且能够返回自定义的错误响应.

But the situation is different on IE 11. When I open the html file there, it first asks me permissions to enable scripts. After I agree, then IE 11 ignores the fact that there are no CORS headers present and it doesn't disrupt the request, thus my exception handler receives IsTopLevel = true and is able to return a customised error response.

我想,这应该在 Web API 核心中修复 - 即使我抛出异常,CORS 仍然应该能够启动并发送其标头,因此浏览器会接受响应.我已经创建了一个最小的测试用例应用程序,我会将它发送给 CodePlex 上的 ASP.NET 团队.测试项目链接.(项目 zip 文件将被标记,只需单击下载并忽略该文件夹中的所有其他文件)

I guess, this should be fixed in the Web API core - even if I throw exceptions, CORS should still be able to kick in and send its headers, so the browser accepts the response. I have created a minimal test case application and I'll send it to the ASP.NET Team on CodePlex. Link to the test project. (the project zip file will be marked, just click Download and ignore all the other files in that folder)

推荐答案

我找到了困惑的根源.

看来,WebAPI 默认使用这个异常处理程序:

It seems, WebAPI by default is using this exception handler:

https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/ExceptionHandling/DefaultExceptionHandler.cs

它与本文中建议的异常处理有很大不同:

and it has major differences from the suggested exception handling in this article:

http://www.asp.net/web-api/overview/web-api-routing-and-actions/web-api-global-error-handling

见附录:基类详解"一章,其中给出了默认异常基类的代码.

see chapter "Appendix: Base Class Details", where the code of default exception base class is given.

在示例中,它检查 IsOutermostCatchBlock(现在似乎已移至 exceptionContext.CatchBlock.IsTopLevel)以查看是否需要处理异常.启用 CORS 后,由于我上面描述的原因,这种方法将不起作用.ASP.NET 团队表示,这种行为是设计使然,他们不会改变任何东西.

In the example it checks for IsOutermostCatchBlock (which now seems to be moved to exceptionContext.CatchBlock.IsTopLevel) to see if it's time to handle the exception. When CORS is enabled, such approach won't work for the reasons I described above. ASP.NET team said, this behavior is by design and they won't change anything.

我希望有经验的人会写一篇最新的文章,说明在使用和不使用 CORS 的情况下以正确的方式处理异常.

I hope someone experienced will write an up-to-date article with instructions for doing exception handling the right way with and without CORS.

目前,我在我的代码中看到了两种解决此问题的方法:

Currently I see two ways to work around this in my code:

  • 不要从System.Web.Http.ExceptionHandling.ExceptionHandler继承我的自定义异常处理程序,而是直接实现IExceptionHandler

  • don't inherit my custom exception handler from System.Web.Http.ExceptionHandling.ExceptionHandler but implement IExceptionHandler directly

如果继承自System.Web.Http.ExceptionHandling.ExceptionHandler,则覆盖ShouldHandle 方法并始终返回 true,因为 CatchBlock.IsTopLevel 可能永远不会有 true 值.

if inheriting from System.Web.Http.ExceptionHandling.ExceptionHandler, override ShouldHandle method and return true always because CatchBlock.IsTopLevel might never have true value.

我尝试了两种方法,它们似乎工作正常.

I tried both apporaches and they seem to work fine.

这篇关于启用 CORS 时,ASP.NET Web API 自定义异常处理程序中的异常永远不会到达顶级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆