CORS,网页API,IE8,邮政复杂的数据 [英] Cors, Web Api, IE8, Post Complex Data

查看:113
本文介绍了CORS,网页API,IE8,邮政复杂的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于我的工作环境的一部分,我们需要支持IE8,但想用技术的前进,特别是CORS。

我无法张贴复杂对象在一个IE8 CORS服务。对象为null。下面是重现的步骤。如果需要,我可以将项目上传到GitHub上。

我已经创建了一个新的项目mvc4。增加了一个API控制器。并提出以下修改。

要支持preflight复杂CORS电话(Global.asax中):

 保护无效的Application_BeginRequest()
    {
        //这是需要为preflight消息
        //http://stackoverflow.com/questions/13624386/handling-cors-$p$pflight-requests-to-asp-net-mvc-actions
        如果(Request.Headers.AllKeys.Contains(起源)及和放大器; Request.HttpMethod ==OPTIONS){Response.Flush(); }
    }

来源:处理CORS preflight请求ASP.NET MVC动作

要支持纯文本/(IE8仅发送文字/平原CORS)(Global.asax中):

 保护无效的Application_Start()
    {
        //这是需要支持文本/纯
        HttpConfiguration配置= GlobalConfiguration.Configuration;
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(新MediaTypeHeaderValue(text / plain的));
        config.Formatters.Remove(config.Formatters.FormUrlEn codedFormatter);
        config.Formatters.Remove(config.Formatters.XmlFormatter);        ...
    }

来源:<一个href=\"http://stackoverflow.com/questions/15621112/posting-text-plain-as-a-complex-object-in-webapi-with-cors\">Posting text / plain的作为的WebAPI一个复杂的对象,CORS

要支持不仅仅是动词(放/后的/ etc)(WebApiConfig.cs)等附加功能名称

 公共静态无效的注册(HttpConfiguration配置)
    {
        config.Routes.MapHttpRoute(
            名称:APICustom
            routeTemplate:API / {控制器} / {行动} / {ID}
            默认:新{ID = RouteParameter.Optional}
        );        ...
    }

为了支持CORS(web.config文件)

 &LT; httpProtocol&GT;
   &LT; customHeaders&GT;
     &LT;! - CORS - &GT;
     &LT;添加名称=访问控制允许来源VALUE =*/&GT;
     &LT;添加名称=访问控制 - 允许 - 头VALUE =Content-Type的/&GT;
   &LT; / customHeaders&GT;
&LT; / httpProtocol&GT;

API控制器,​​我叫PersonController.cs

 公共类PersonController:ApiController
{    公开名单&LT;串GT;得到()
    {
        清单&LT;串GT; S =新的List&LT;串GT;();
        s.Add(S);
        s.Add(T);
        s.Add(U);
        返回S;
    }    [序列化()]
    公共类BaseReply
    {
        公共BOOL成功= TRUE;
        公共字符串错误;
    }
    [序列化()]
    公共类UpdateSomethingReply:BaseReply
    {
        公共UpdateSomethingRequest请求;
        公开名单&LT;串GT;东西=新的List&LT;串GT;();
    }
    [序列化()]
    公共类UpdateSomethingRequest
    {
        公众诠释hasInt;
        公共字符串hasString;
    }
    // [FromBody]
    [HttpPost]
    公共UpdateSomethingReply UpdateSomething([FromBody] UpdateSomethingRequest要求)
    {
        绳体= Request.Content.ReadAsStringAsync()结果。
        UpdateSomethingReply回复=新UpdateSomethingReply();
        reply.request =请求;        reply.stuff.Add(V);
        reply.stuff.Add(W);
        reply.stuff.Add(×);
        返回回复;
    }

这是对服务的改变的程度。所以下次我创建了一个客户端。这也是一个mvc4项目。 pretty基本的东西在这里。

要与CORS(index.cshtml)填充工具IE8:

 &LT;脚本SRC =〜/脚本/ jQuery.XDomainRequest.js&GT;&LT; / SCRIPT&GT;

来源: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest

要调用CORS服务

  $(文件)。就绪(函数(){
        $。当(
          $阿贾克斯({
              网址:urls.person.UpdateSomething,
              类型:'后',
              的contentType:应用/ JSON的;字符集= UTF-8,
              数据类型:JSON,
              数据:JSON.stringify({
                  hasInt:1,
                  hasString:U
              })
          })
        )
        .fail(功能(jqXHR,textStatus,errorThrown){
        })
        .done(功能(数据){
            的console.log(JSON.stringify(数据));
        });        $。当(
          $阿贾克斯({
              网址:urls.person.Get,
              数据类型:JSON
          })
        )
        .fail(功能(jqXHR,textStatus,errorThrown){
        })
        .done(功能(数据){
            的console.log(JSON.stringify(数据));
        });        $。当(
          $阿贾克斯({
              网址:urls.person.UpdateSomething,
              类型:'后',
              的contentType:text / plain的,
              数据类型:JSON,
              数据:JSON.stringify({
                  hasInt:1,
                  hasString:U
              })
          })
        )
        .fail(功能(jqXHR,textStatus,errorThrown){
        })
        .done(功能(数据){
            的console.log(JSON.stringify(数据));
        });
    });

正如我前面所述的所有3个电话完成在IE8。但在服务的请求对象为null在IE8和Firefox中被填充,甚至当我强制内容类型为text / plain的

IE8控制台输出:

  {请求:空,东西:V,W,X],成功:真实的错误:空}

火狐控制台输出:

<$p$p><$c$c>{\"request\":{\"hasInt\":1,\"hasString\":\"u\"},\"stuff\":[\"v\",\"w\",\"x\"],\"successful\":true,\"error\":null}

更新2013年9月25日

我可以证实,身体被发送,但不被通过网络API解析。如果我添加下面的技巧,将返回数据如预期。在Firefox中身体会是空的,请求对象填充。在IE8身体仍然包含的内容和要求为null。

  [HttpPost]
    公共UpdateSomethingReply UpdateSomething(UpdateSomethingRequest要求)
    {
        如果(要求== NULL和放大器;&安培;!Request.Content.ReadAsStringAsync()结果=)
        {
            请求= JsonConvert.DeserializeObject<UpdateSomethingRequest>(Request.Content.ReadAsStringAsync().Result);
       }        UpdateSomethingReply回复=新UpdateSomethingReply();
        reply.request =请求;
        reply.body = Request.Content.ReadAsStringAsync()结果。
        reply.headers = Request.Headers.ToString();
        reply.stuff.Add(V);
        reply.stuff.Add(W);
        reply.stuff.Add(×);
        返回回复;
    }


解决方案

这里的code我在说什么。作为一个新的类来创建此,我在的WebAPI项目中创建一个文件夹DelegatingHandlers(不过话又说回来了,我也有一个过滤器文件夹,模型绑定文件夹...)

我已经包含的意见,你可以轻松地删除吨。

下面的假设IE 8/9总是会发出JSON的数据。如果您的WebAPI实现允许内容协商,并且要包含该功能对于IE8 / 9,那么你显然需要if语句添加一些到低于code,但是这应该是绰绰有余,让你去。我个人只是说,我只能从IE 8/9接受JSON。

 命名空间REDACTED.WebApi.DelegatingHandlers
{
    使用System.Net.Http;
    使用System.Net.Http.Headers;
    使用的System.Threading;
    使用System.Threading.Tasks;    ///&LT;总结&gt;
    ///给人的的WebAPI来处理与嵌入式JSON数据XDomainRequest对象的能力。
    ///&LT; /总结&gt;
    公共类XDomainRequestDelegatingHandler:DelegatingHandler
    {
        保护覆盖任务&LT; Htt的presponseMessage&GT; SendAsync(
            HTT prequestMessage请求的CancellationToken的CancellationToken)
        {
            // XDomainRequest对象设置内容类型为空,这是一个unchangable设置。
            //微软规范规定XDomainRequest总是有纯文本/的contentType中,但文档是错误的。
            //很明显,这打破了几乎所有的规范,所以它原来的唯一的可扩展性
            //点​​来处理这个请求命中的WebAPI框架之前,因为我们在这里做。            //读取创建该对​​象XDomainRequest开发商道歉,在这里看到:
            // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx            //按照国际标准,一个空的内容类型应该是导致应用程序/ octect流(拼写错误?)
            //但是,因为这是这样一个边缘的情况下,框架的WebAPI不转换为我们我们打这个点之前。不太可能,
            //但可能在将来的版本Web.Api,我们还需要嗅出这里的octect头。
            如果(request.Content.Headers.ContentType == NULL)
            {
                request.Content.Headers.ContentType =新MediaTypeHeaderValue(应用/ JSON);
            }            返回base.SendAsync(请求的CancellationToken);
        }
    }
}

我WebAPIConfig文件看起来如下:

 公共静态无效的注册(HttpConfiguration配置)
        {
             //正常config.Routes语句去这里            //反序列化/型号绑定IE 8和9 Ajax的请求
            config.MessageHandlers.Add(新XDomainRequestDelegatingHandler());
        }

然后,以确保我的帖子电话都是IE 8和9兼容的,在我的JS我把下面的(虽然很明显,你只需要包括这个,如果你也在消耗自己的API)

  esbPost:功能(apiUrl,apiData,fOnSuccess,fOnFailure){
    $ .support.cors = TRUE; //不知道,我需要这个。    VAR testModernAjax =功能(){
        如果(window.XMLHtt prequest){
            VAR testRequest =新XMLHtt prequest;            // IE 8/9用jQuery可以创建XMLHtt prequest对象,但唯一的现代化
            // CORS实现浏览器(一切+ IE10)包括withCredentials规范。
            如果(在testRequest'withCredentials'){
                返回true;
            }
            返回false;
        }
        返回false;
    };    VAR testMsieAjax =功能(){
        如果(window.XDomainRequest){
            返回true;
        }
        返回false;
    };    //所有的浏览器和IE 10
    如果(testModernAjax()){
        $阿贾克斯({
            网址:apiUrl,
            输入:POST,
            数据类型:JSON,
            数据:apiData,
            成功:函数(结果){
                如果(fOnSuccess){
                    fOnSuccess(结果);
                }
            },
            错误:功能(jqXHR,textStatus,errorThrown){
                如果(fOnFailure){
                    fOnFailure(jqXHR,textStatus,errorThrown);
                }
            }
        });
    // IE 8/9
    }否则如果(testMsieAjax()){
        VAR XDR =新XDomainRequest();
        xdr.onload =功能(){
            VAR parsedResponse = $ .parseJSON(xdr.responseText);
            如果(fOnSuccess){
                fOnSuccess(parsedResponse);
            }
        };
        xdr.onerror =功能(){
            如果(fOnFailure){
                fOnFailure();
            }
        };
        xdr.onprogress =函数(){};
        xdr.open(后,apiUrl);
        xdr.send(JSON.stringify(apiData));
    }其他{
        // IE 7只能做AJAX调用通过闪光/ IFRAME漏洞,前面不包括Ajax支持。
        抛出新的此浏览器不支持此解决方案。;
    }
},

就个人而言,我使用JSONP为入眼,而不是使用PUTS或删除任何,所以这是足以让我。如果我要重新做这个项目,我会用PUTS和删除。为了让IE 8/9手柄跨域PUTS并删除其显然是常见的做法,包括对数据的新节点发送,或在标题,叫类型的一些变种,并使用字符串PUT或DELETE 。我不知道在哪里我嗅出了这一点,虽然。

启用CORS是因为把以下在Web.config一样容易。

 &LT; system.webServer&GT;
    &LT; httpProtocol&GT;
      &LT; customHeaders&GT;
        &LT;添加名称=访问控制允许来源VALUE =*/&GT;
        &LT;! - &LT;添加名称=访问控制允许的方法VALUE =GET,POST,PUT,DELETE,OPTIONS/&GT; - &GT;
      &LT; / customHeaders&GT;
    &LT; / httpProtocol&GT;

正如你可以在上面看到的评论,你还可以限制借原网址。(*)和请求(PUT,POST等)类型CORS。完全让这样的东西 完全不必要。 <一href=\"http://rions$c$c.word$p$pss.com/2013/06/03/using-cors-in-asp-net-webapi-without-being-a-rocket-scientist/\"相对=nofollow>这家伙的博客提供了一个很好的演练。

这就是字面上所有你需要做一个全新的WebAPI项目,使其同时支持CORS和IE 8/9。

As part of my work environment we need to support IE8, but would like to move forward with technology, specifically CORS.

I'm having trouble posting complex objects to a cors service in ie8. The object is null. Below are the steps to reproduce. If needed i can upload the project to github.

I've created a new mvc4 project. Added a API Controller. And made the following changes.

To Support preflight complex cors calls (global.asax):

    protected void Application_BeginRequest()
    {
        //This is needed for the preflight message
        //http://stackoverflow.com/questions/13624386/handling-cors-preflight-requests-to-asp-net-mvc-actions
        if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")  {  Response.Flush(); }
    }

Source: Handling CORS Preflight requests to ASP.NET MVC actions

To Support text/plain (ie8 only sends text/plain with cors)(global.asax):

    protected void Application_Start()
    {
        //This is needed to support text/plain
        HttpConfiguration config = GlobalConfiguration.Configuration;
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
        config.Formatters.Remove(config.Formatters.FormUrlEncodedFormatter);
        config.Formatters.Remove(config.Formatters.XmlFormatter); 

        ...
    }

Credit: Posting text/plain as a complex object in WebAPI with CORS

To Support additional function names other than just verbs (put/post/etc) (WebApiConfig.cs)"

    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "APICustom",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        ...
    }

To support cors (web.config)

<httpProtocol>
   <customHeaders>
     <!-- cors -->
     <add name="Access-Control-Allow-Origin" value="*" />
     <add name="Access-Control-Allow-Headers" value="Content-Type" />
   </customHeaders>
</httpProtocol>

API Controller, I called PersonController.cs

 public class PersonController : ApiController
{

    public List<string> Get()
    {
        List<string> s = new List<string>();
        s.Add("s");
        s.Add("t");
        s.Add("u");
        return s;
    }



    [Serializable()]
    public class BaseReply
    {
        public bool successful = true;
        public string error;
    }
    [Serializable()]
    public class UpdateSomethingReply:  BaseReply
    {
        public UpdateSomethingRequest request;
        public List<string> stuff = new List<string>();
    }
    [Serializable()]
    public class UpdateSomethingRequest
    {
        public int hasInt;
        public string hasString;
    }
    //[FromBody] 
    [HttpPost]
    public UpdateSomethingReply UpdateSomething([FromBody] UpdateSomethingRequest request)
    {
        string body = Request.Content.ReadAsStringAsync().Result;
        UpdateSomethingReply reply = new UpdateSomethingReply();
        reply.request = request;

        reply.stuff.Add("v");
        reply.stuff.Add("w");
        reply.stuff.Add("x");
        return reply;
    }

That is the extent on the changes on the service. So next I create a client. This is also an mvc4 project. Pretty basic stuff here.

To polyfill ie8 with cors (index.cshtml):

<script src="~/Scripts/jQuery.XDomainRequest.js"></script>

Source: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest

To call the cors service

 $(document).ready(function () {
        $.when(
          $.ajax({
              url: urls.person.UpdateSomething,
              type: 'post',
              contentType: "application/json; charset=utf-8",
              dataType: 'json',
              data: JSON.stringify({
                  hasInt: 1,
                  hasString: "u"
              })
          })
        )
        .fail(function (jqXHR, textStatus, errorThrown) {
        })
        .done(function (data) {
            console.log(JSON.stringify(data));
        });

        $.when(
          $.ajax({
              url: urls.person.Get,
              dataType: 'json'
          })
        )
        .fail(function (jqXHR, textStatus, errorThrown) {
        })
        .done(function (data) {
            console.log(JSON.stringify(data));
        });

        $.when(
          $.ajax({
              url: urls.person.UpdateSomething,
              type: 'post',
              contentType: "text/plain",
              dataType: 'json',
              data: JSON.stringify({
                  hasInt: 1,
                  hasString: "u"
              })
          })
        )
        .fail(function (jqXHR, textStatus, errorThrown) {
        })
        .done(function (data) {
            console.log(JSON.stringify(data));
        });
    });

As i stated earlier all 3 calls complete in ie8. But the request object in the service is null in ie8 and in firefox it is populated, even when i force the content-type to be text/plain

IE8 Console Output:

{"request":null,"stuff":["v","w","x"],"successful":true,"error":null}

Firefox Console Output:

{"request":{"hasInt":1,"hasString":"u"},"stuff":["v","w","x"],"successful":true,"error":null}

Update 9/25/2013

I can confirm that the body is being sent, but isn't being parsed by web api. If I add the following hack it will return the data as expected. In firefox the body will be empty and the request object is populated. In ie8 the body still contains the contents and the request is null.

    [HttpPost]
    public UpdateSomethingReply UpdateSomething(UpdateSomethingRequest request)
    {
        if (request == null && Request.Content.ReadAsStringAsync().Result !="")
        {
            request = JsonConvert.DeserializeObject<UpdateSomethingRequest>(Request.Content.ReadAsStringAsync().Result);
       }

        UpdateSomethingReply reply = new UpdateSomethingReply();
        reply.request = request;
        reply.body=Request.Content.ReadAsStringAsync().Result;
        reply.headers = Request.Headers.ToString();
        reply.stuff.Add("v");
        reply.stuff.Add("w");
        reply.stuff.Add("x");
        return reply;
    }

解决方案

here's the code I was talking about. Create this as a new class, I created a DelegatingHandlers folder in my WebAPI project (but then again, I also have a filters folder, A model bindings folder...)

I've included TONS of comments that you could easily remove.

The below assumes IE 8/9 will always be sending "JSON" data. If your webAPI implementation allows content negotiation, and you want to include that feature for IE8/9 then you will obviously need to add a few if statements to the below code, but this should be more than enough to get you going. I personally just stated that I only accept JSON from IE 8/9.

namespace REDACTED.WebApi.DelegatingHandlers
{
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Threading;
    using System.Threading.Tasks;

    /// <summary>
    /// Gives the WebAPI the ability to handle XDomainRequest objects with embedded JSON data.
    /// </summary>
    public class XDomainRequestDelegatingHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // XDomainRequest objects set the Content Type to null, which is an unchangable setting.
            // Microsoft specification states that XDomainRequest always has a contenttype of text/plain, but the documentation is wrong.
            // Obviously, this breaks just about every specification, so it turns out the ONLY extensibility
            // point to handle this is before the request hits the WebAPI framework, as we do here.

            // To read an apology from the developer that created the XDomainRequest object, see here: 
            // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx

            // By international specification, a null content type is supposed to result in application/octect-stream (spelling mistake?),
            // But since this is such an edge case, the WebAPI framework doesn't convert that for us before we hit this point.  It is unlikely, 
            // but possible that in a future Web.Api release, we will need to also sniff here for the octect header.
            if (request.Content.Headers.ContentType == null)
            {
                request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }

            return base.SendAsync(request, cancellationToken);
        }
    }
}

My WebAPIConfig file looks as follows:

        public static void Register(HttpConfiguration config)
        {
             // Normal config.Routes statements go here

            // Deserialize / Model Bind IE 8 and 9 Ajax Requests
            config.MessageHandlers.Add(new XDomainRequestDelegatingHandler());
        }

Then to make sure my POST calls were IE 8 and 9 compliant, in my JS I put the following (though obviously you only need to include this if you are also consuming your own API)

esbPost: function (apiUrl, apiData, fOnSuccess, fOnFailure) {
    $.support.cors = true; // Not sure that I need this.

    var testModernAjax = function () {
        if (window.XMLHttpRequest) {
            var testRequest = new XMLHttpRequest;

            // IE 8 / 9 with jQuery can create XMLHttpRequest objects, but only modern 
            // CORS implementing browsers (everything + IE10) include the withCredentials specification.
            if ('withCredentials' in testRequest) {
                return true;
            }
            return false;
        }
        return false;
    };

    var testMsieAjax = function () {
        if (window.XDomainRequest) {
            return true;
        }
        return false;
    };

    //All browsers, and IE 10
    if (testModernAjax()) {
        $.ajax({
            url: apiUrl,
            type: 'POST',
            dataType: 'json',
            data: apiData,
            success: function (result) {
                if (fOnSuccess) {
                    fOnSuccess(result);
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                if (fOnFailure) {
                    fOnFailure(jqXHR, textStatus, errorThrown);
                }
            }
        });
    //IE 8 / 9
    } else if (testMsieAjax()) {
        var xdr = new XDomainRequest();
        xdr.onload = function () {
            var parsedResponse = $.parseJSON(xdr.responseText);
            if (fOnSuccess) {
                fOnSuccess(parsedResponse);
            }
        };
        xdr.onerror = function () {
            if (fOnFailure) {
                fOnFailure();
            }
        };
        xdr.onprogress = function () { };
        xdr.open("post", apiUrl);
        xdr.send(JSON.stringify(apiData));
    } else {
        // IE 7 can only do AJAX calls through a flash/iframe exploit, earlier do not include ajax support.
        throw new 'This browser is unsupported for this solution.';
    }
},

Personally, I'm using JSONP for GETs, and not using PUTS or DELETES whatsoever, so that's sufficient for me. If I were to do this project over again, I would use PUTS and DELETES. To make IE 8 / 9 handle cross domain PUTS and DELETES its apparently common practice to include a new node on the data being sent, or in the header, called some variant of "Type", and use a string "PUT" or "DELETE". I'm not sure where I'd sniff that out though.

Enabling CORS was as easy as putting the following in the Web.Config.

<system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <!--<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />-->
      </customHeaders>
    </httpProtocol>

As you can see in the above comment, you can also restrict CORS by originating url (the *) and the type of request (put, post, etc). Totally makes stuff like this completely unnecessary. This guy's blog gives a really good walkthrough.

And that's literally all you need to do to a brand new WebAPI project to make it Support both CORS and IE 8/9.

这篇关于CORS,网页API,IE8,邮政复杂的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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