带有 CORS 和 IE9 的 ASP Web API POST 请求(XDomainRequest 对象)

Posted

技术标签:

【中文标题】带有 CORS 和 IE9 的 ASP Web API POST 请求(XDomainRequest 对象)【英文标题】:ASP Web API POST request with CORS and IE9 (XDomainRequest object) 【发布时间】:2013-09-28 15:28:44 【问题描述】:

我一直在努力让 jquery.ajax 与 ie9 一起工作。所以我有一个实现 CORS 的 ASP Web API 2 Rest API。来自所有浏览器的 CORS 请求都有效。 IE9 没有工作,因为它使用 XDomainRequest。我通过为 IE9 自定义实现 ajaxTransport 来设法让它也能正常工作。

现在 GET 请求似乎工作正常。但是当我从 IE9 发出 post 请求时,我收到 HTTP 错误 415 - 不支持的媒体类型。

我已将内容类型设置为:“application/json”,并且我也尝试过“application/x-www-form-urlencoded”,但据我了解,XDomainRequest 不支持所有带有自定义标头的内容?有人知道是否需要在 WebAPI 上设置一些特定的东西还是我需要调整请求?

我的请求如下所示:

                        $.ajax(
                        url: hostname + "/api/DDC/Book",
                        type: "POST",

                        contentType: "application/json",
                        data: 
                            DealID: function () 
                                return viewModel.get("DealID");
                            ,
                            LocationID: function () 
                                return viewModel.get("LocationID");
                            ,
                            Time: function () 
                                return viewModel.get("selectedDateTime.Time");
                            

                        
                    )

在服务器上我有这个:

[HttpPost("DDC/Book")]
    [EnableCors(origins: "*", headers: "*", methods: "POST, GET, OPTIONS, PUT, DELETE")]
    public dynamic Post(BookModel model)
    
       .........

当我在 IE 调试器中分析失败的请求时,这是发出的请求标头:

Key Value
Request POST //api/DDC/Book HTTP/1.1
Accept  */*
Origin  http://myurl.com
Accept-Language hr-HR
Accept-Encoding gzip, deflate
User-Agent  Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host    www.somehost.com
Content-Length  55
DNT 1
Connection  Keep-Alive
Cache-Control   no-cache

我真的在这里失去了所有希望,IE 让我发疯了(该死的微软:D),所以任何帮助或建议都非常受欢迎。

编辑:从更多的研究中我发现 WebAPI 需要一个内容类型才能工作,而 XDomainRequest 没有发送一个。因此,我看到的唯一解决方案是在未设置任何内容时过于调整我的 webapi 以具有默认的内容类型。不过还不知道怎么弄

EDIT2:通过将我所有的 POST 转换为 GET 暂时破解了我的方法,不知道这有多聪明,但我现在看不出有更大的问题,所以在我解决问题之前会一直这样做

【问题讨论】:

【参考方案1】:

设法自己解决了。正如 Ray Nicholus 所指出的,当没有 Content-Type 时,ASP Web API 默认为“application/octet-stream”Content-Type。我需要默认的“application/x-www-form-urlencoded”。

我通过编写自己的简单消息处理程序来实现这一点,该处理程序检查传入的请求“Content-Type”,如果没有任何内容,它会添加一个“application/x-www-form-urlencoded”。

这是代码:

public class DefaultContentTypeMessageHandler : DelegatingHandler

    protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
    
        if (request.Content.Headers.ContentType == null)
            request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");


        var response = await base.SendAsync(request, cancellationToken);


        return response;
    


更新:

正如 Robert Christ 在下面的评论中所写,我正在为那些以前没有使用过消息处理程序的人扩展一下答案:

对于初看不明白的,DelegatingHandlers 允许您在请求/响应对象真正命中之前修改它们 WebAPI 框架内部。框架中没有其他内容 允许您在模型绑定之前修改传入的请求,而无需 实际编写自定义模型绑定器(呃)。所以相反,在这里,你 可以嗅出一个空内容类型(这是由缺点保证 在 XDomainRequest 规范中),将其更新为 xml 或 json,您将 能够正确解析传入的请求。

编写完消息处理程序后,您需要将其注册到 WebAPI。您可以在 WebApiConfig 类中执行此操作:

public static class WebApiConfig

    public static void Register(HttpConfiguration config)
    

        config.MessageHandlers.Add(new DefaultContentTypeMessageHandler());
        // Rest of your code

     
 

【讨论】:

这几乎正是我最终所做的。对于那些乍一看不明白的人,DelegatingHandlers 允许您在请求/响应对象真正触及 WebAPI 框架内部之前对其进行修改。框架中没有其他任何东西可以真正让您在模型绑定之前修改传入的请求,而无需实际编写自定义模型绑定器(呃)。因此,在这里,您可以嗅出 null 内容类型(XDomainRequest 规范中的缺陷保证了这一点),将其更新为 xml 或 json,您将能够正确解析传入的请求。 天哪!这个答案让我很开心。我在 IE8/9 上遇到了同样的问题,效果很好。【参考方案2】:

只是为了确认您已经在几次更新中编辑了您的问题:是的,XDomainRequest 在请求中不包含 Content-Type 标头。正如您现在可能知道的那样,您无法通过此传输设置任何标头。

对于大多数服务器端框架来说,缺少 Content-Type 尤其成问题,因为这意味着它们将无法自动解析响应的内容。在没有 Content-Type 标头的情况下,RFC 2616 说正文被假定为 application/octet-stream,在这种情况下这可能不是您想要的。因此,在这种情况下,您需要通过硬编码相关请求的预期 Content-Type 来“手动”解析请求正文服务器端。

我强烈建议您不要简单地将所有 POST 转换为 GET。根据 RFC 2616,GET 请求应该是“安全的”。只需将所有 POST 重命名为 GET,您就不再遵循 GET 请求的定义和接受的语义。换句话说,不要这样做。

【讨论】:

感谢您的回复。是的,我知道关于 GET 并且非常同意,它只是一个临时的 hack 让它运行,直到我修复它,它还不是生产代码。对于将要更改的生产阶段。它在小部件 API 中只有 2 个 POST。 application/octet-stream 信息是我不知道但很有用的。 换句话说,如果不清楚,您需要您的服务器代码知道请求正文的格式,以便它可以手动解析这些请求,因为 XDR 不'不允许您在请求中包含 Content-Type。【参考方案3】:

Dennis 上面的回答使用仅在 .NET 4.5 中可用的异步。对于 .NET 4 和可能更低的版本,请改用以下委托处理程序:

 public class DefaultContentTypeMessageHandler : DelegatingHandler
 
      protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
      
          if (request.Method == HttpMethod.Post && request.Content.Headers.ContentType == null)
          
              request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
          
          return base.SendAsync(request, cancellationToken);
      
 

另外,不要忘记您的 USING 语句,您将需要:

using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

【讨论】:

以上是关于带有 CORS 和 IE9 的 ASP Web API POST 请求(XDomainRequest 对象)的主要内容,如果未能解决你的问题,请参考以下文章

尽管有 web.config 条目,但带有 ASP.NET WCF 服务的 jQuery AJAX CORS 无法正常工作

ASP.NET Core:带有 OPTIONS 异常的 Windows 身份验证(CORS 预检)

无法在 ASP.Net Core Web api 中启用 CORS

web.config 中的 <cors> 标记与带有自定义标头的 httpProtocol 之间有区别吗?

使集成的安全性和 CORS 与 Asp.Net、Angular 6 和 IIS 一起工作

CORS 错误,无法从 asp.net 核心后端接收数据