3

私の作業環境の一部として、IE8 をサポートする必要がありますが、テクノロジ、特に CORS を進めたいと考えています。

複雑なオブジェクトを ie8 の cors サービスにポストするのに問題があります。オブジェクトがヌルです。以下、再現手順です。必要に応じて、プロジェクトを github にアップロードできます。

新しい mvc4 プロジェクトを作成しました。API コントローラーを追加しました。そして、以下の変更を行いました。

プリフライトの複雑な cors 呼び出し (global.asax) をサポートするには:

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

出典: ASP.NET MVC アクションへの CORS プリフライト リクエストの処理

text/plain をサポートするには (ie8 は、cors を含む text/plain のみを送信します)(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); 

        ...
    }

クレジット: CORS を使用して WebAPI で text/plain を複合オブジェクトとして投稿する

動詞以外の追加の関数名 (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 }
        );

        ...
    }

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 コントローラー、私は 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;
    }

それがサービスの変更の範囲です。次に、クライアントを作成します。これも mvc4 プロジェクトです。ここではかなり基本的なもの。

ie8 を cors (index.cshtml) でポリフィルするには:

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

ソース: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest

cors サービスを呼び出すには

 $(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));
        });
    });

前に述べたように、3 つの呼び出しはすべて ie8 で完了します。しかし、サービスのリクエスト オブジェクトは ie8 では null であり、Firefox では、コンテンツ タイプをテキスト/プレーンに強制しても、データが取り込まれます。

IE8 コンソール出力:

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

Firefox コンソール出力:

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

2013 年 9 月 25 日更新

本文が送信されていることを確認できますが、Web API によって解析されていません。次のハックを追加すると、期待どおりにデータが返されます。Firefox では、本文は空になり、リクエスト オブジェクトが入力されます。ie8 では、ボディにはまだコンテンツが含まれており、リクエストは 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;
    }
4

2 に答える 2

3

これが私が話していたコードです。これを新しいクラスとして作成し、WebAPI プロジェクトに DelegatingHandlers フォルダーを作成しました (ただし、filters フォルダー、モデル バインディング フォルダーもあります...)

簡単に削除できる大量のコメントを含めました。

以下は、IE 8/9 が常に「JSON」データを送信することを前提としています。WebAPI の実装でコンテンツ ネゴシエーションが許可されていて、IE8/9 にその機能を含めたい場合は、以下のコードに if ステートメントをいくつか追加する必要がありますが、これで十分です。個人的には、IE 8/9 からの JSON のみを受け入れると述べました。

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);
        }
    }
}

私の WebAPIConfig ファイルは次のようになります。

        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());
        }

次に、POST 呼び出しが IE 8 および 9 に準拠していることを確認するために、JS に次のコードを追加しました (ただし、独自の 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.';
    }
},

個人的には、GET には JSONP を使用しており、PUTS や DELETES はまったく使用していないので、それで十分です。このプロジェクトをやり直す場合は、PUTS と DELETES を使用します。IE 8 / 9 でクロスドメインの PUTS と DELETES を処理するには、送信されるデータまたはヘッダーに新しいノードを含めるのが明らかに一般的な方法であり、「Type」のバリアントと呼ばれ、文字列「PUT」または「DELETE」を使用します。 "。どこでそれを嗅ぎ分けるかはわかりません。

CORS を有効にするのは、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>

上記のコメントでわかるように、元の URL (*) と要求の種類 (put、post など) によって CORS を制限することもできます。このようなものは 完全不要になります。 この男のブログは本当に良いウォークスルーを提供します。

CORS と IE 8/9 の両方をサポートするために、まったく新しい WebAPI プロジェクトに対して行う必要があるのは、文字通りこれだけです。

于 2013-10-01T17:57:16.647 に答える
0

私が別の解決策を見つけるか、IE8 のサポートをやめるまでは、ここで認められたハックです。これを思いついた同僚の功績です。

  1. global.asax の text/plain のサポートを削除します。ie8 によって送信されるヘッダーはすべて null です。コメントで説明されているように、リクエストの本文は自動的に解析されません。中身は体内に残ります。通常 (Firefox など)、ボディはリクエスト オブジェクトに解析され、空の文字列に置き換えられます。
  2. App_Start で、GenericBinder というクラスを作成します

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    using Newtonsoft.Json;
    using System.Web.Http.Controllers;
    using System.Web.Http.ModelBinding;
    namespace Admin2
    {
      public class GenericBinder : IModelBinder
      {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            bindingContext.Model = JsonConvert.DeserializeObject(actionContext.Request.Content.ReadAsStringAsync().Result, bindingContext.ModelType);
            return true;
        }
      }
    }
    
  3. パーソンコントローラーを次のように変更します

    using System.Web.Http.ModelBinding;
    
    ...
    
    [HttpPost]
    public UpdateSomethingReply UpdateSomething([ModelBinder(typeof(GenericBinder))] UpdateSomethingRequest request)
    {
      UpdateSomethingReply reply = new UpdateSomethingReply();
      reply.request = request;
      reply.stuff.Add("v");
      reply.stuff.Add("w");
      reply.stuff.Add("x");
      return reply;
    }
    

IE8 では、複雑なデータを送信できるようになりました。

于 2013-09-25T17:01:59.467 に答える