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

查看:13
本文介绍了启用 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() 方法.即使我从本地 html 文件向 IIS Express localhost 上的 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天全站免登陆