使用CORS和IE9(XDomainRequest对象)的ASP Web API POST请求

时间:2013-09-23 16:24:35

标签: asp.net-mvc jquery asp.net-web-api cors

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

现在GET请求似乎工作正常。但是,当我从IE9发出一个帖子请求时,我收到一个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来暂时破解我的方式,不知道这有多聪明,但我现在看到它没有更大的问题,所以它会解决问题

3 个答案:

答案 0 :(得分:8)

管理自己解决它。正如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;
    }

}

更新:

正如罗伯特·基督在下面的评论中所写,我正在为那些之前没有使用过消息处理程序的人提供一些答案:

  

对于那些乍看之下不了解的人,DelegatingHandlers   允许您在请求/响应对象真正命中之前修改它们   WebAPI框架内部。框架中没有其他东西真的   允许您在模型绑定之前修改传入请求,而不是   实际上写自定义模型粘合剂(eugh)。所以相反,在这里,你   可以嗅出一个null内容类型(由缺点保证   在XDomainRequest规范中,将其更新为xml或json,你将会是   能够正确解析传入的请求。

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

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {

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

     }
 }

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

Dennis上面的回答使用async,它只在.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;