クロスドメイン制約 を XmlHttpRequest level 2 で 回避 (プリフライト)

0 件のコメント

クロスドメイン制約 を 回避 する クライアント & サーバー 実装 を XmlHttpRequest level2 の プリフライト を利用して実現します。

クロスドメイン通信 を実現するには "サーバー側" と "クライアント側" の2カ所に対して手を入れる必要があるかどうか考えます。 ここでは、"クライアント側" と "サーバー側" 両方に手を加えて クロスドメイン通信 を実現する方法を載せます。 具体的には、XmlHttpRequest level2 の プリフライト を利用して クロスドメイン通信 を行えるようにします。

サンプルコード の ダウンロード

想定する クロスドメイン通信 環境

サーバーA から HTML & JavaScript を受信し、足りない リソース を サーバーB から取得する環境を想定します。

GET はそのまま通信されますが、POST, PUT, DELETE は プリフライト (OPTIONS) が自動的に行われます。 つまり、自前で OPTIONS を実行しなくても勝手に実行されるので、自動的に行われる プリフライト通信 に正しい応答処理が行われるようにすれば良いことになります。

サーバー 実装

サーバー側では、"プリフライトの応答" と "サービスの実態" の 2つ を記述します。 サーバー側で確認、実装が必要なのでは以下に示す2ファイル(WebApiConfig.cs、CatalogController.cs)です。

以下にサーバー側で必要な実装の内容、手順を載せます。

  1. OPTIONS へのマッピング を確認。
    /App_Start/WebApiConfig.cs
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
    
  2. プリフライト 用 の コード を 実装。
    /Controllers/CatalogController.cs
    // OPTIONS api/catalog
    public void Options()
    {
        var response = HttpContext.Current.Response;
    
        response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");
        response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.AppendHeader("Access-Control-Allow-Headers", "X-PINGOTHER, application/json");
        response.AppendHeader("Access-Control-Max-Age", "1728000");
    }
    
  3. GET, POST 等 実態 コード を 実装。 (※ここでは POST を サンプルコード として掲載します。その他は、サーバー実装(全体) サンプルコードをご参照ください。)
    /Controllers/CatalogController.cs
    // POST api/catalog
    public string Post([FromBody]string value)
    {
        var response = HttpContext.Current.Response;
        response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");
    
        return "success POST method !!";
    }
    

サーバー実装(全体) サンプルコード

namespace CrossOriginResourceSharing.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using System.Web;

    public class CatalogController : ApiController
    {
        // GET: api/catalog
        public IEnumerable<string> Get()
        {
            var response = HttpContext.Current.Response;
            response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");

            return new string[] { "val1", "val2" };
        }

        // GET api/catalog/5
        public string Get(int id)
        {
            var response = HttpContext.Current.Response;
            response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");

            return "value";
        }

        // POST api/catalog
        public string Post([FromBody]string value)
        {
            var response = HttpContext.Current.Response;
            response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");

            return "success POST method !!";
        }

        // PUT api/catalog/5
        public string Put(int id, [FromBody]string value)
        {
            var response = HttpContext.Current.Response;
            response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");

            return "success PUT method !!";
        }

        // DELETE api/catalog/5
        public void Delete(int id)
        {
            var response = HttpContext.Current.Response;
            response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");
        }

        // OPTIONS api/catalog
        public void Options()
        {
            var response = HttpContext.Current.Response;

            response.AppendHeader("Access-Control-Allow-Origin", "http://www.example.com");
            response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            response.AppendHeader("Access-Control-Allow-Headers", "X-PINGOTHER, application/json");
            response.AppendHeader("Access-Control-Max-Age", "1728000");
        }
    }
}

クライアント 実装

クロスドメイン通信 は、実際に行う前に OPTION 通信で クロスドメイン通信 の許可を取った(プリフライト)のち、 はじめて クロスドメイン通信 ができるようになります。 この事前に行われる OPTION通信 は、自前で実装するのではなく、ブラウザの処理で自動的に処理されるようです。 つまり、以下のような処理の流れになります。

  1. クライアントからクロスドメイン通信を試みる
  2. ブラウザが自動で プリフライト (OPTION通信) を行う
  3. クロスドメイン通信ができそうなので、再度リクエストする

実装してみると、以下のような 単純な サンプルコード になります。 これだけで、OPTION と POST が行われます。

function () {
    $.ajax({
        type: 'POST',
        url: 'http://service.example.com/Service/api/catalog'
    }).done(function (data, textStatus, jqXHR) {
        window.alert(JSON.stringify(data));
    }).fail(function (jqXHR, textStatus, errorThrown) {
        window.alert(errorThrown);
    });
};

今回、以下のサイトを参考にしました。