PostMessageを使ったクロスドメイン通信

JavaScriptはXMLHttpRequestにてセキュリティの関係上、ファイルが置かれているドメインのサーバとしか通信できないという制約(クロスドメイン制約)があります。これを回避するためには、XMLHttpRequest2にてAJAX通信を行う方法があります。XMLHttpRequest2ではサーバー側がAccess-Control-Allow-Originヘッダーを設定していればクロスドメインの制限を回避することが可能です。ただ、これは対応ブラウザでいうと、特にIEで10以降でないと対応していない点で、環境が非常に限定されてしまいます。

XMLHttpRequestにてAJAX通信において、このクロスドメイン制約を突破する一般的な方法がいくつかあります。

PHPやCGIを経由
「JavaScript→PHPorCGI→参照したいファイル」という順に読み込んでいきます。設定が簡単で導入しやすいですが、PHPやCGI等を通信元に設置しなくてはなりません。
Flashを経由
Flashを経由するとCGIやPHPが使えない環境でも動作するメリットがあります。しかし、「crossdomain.xml」という設定ファイルを読み込みたいデータが置いてあるサーバ側に設置する必要があります。またクライアント側にFlashPlayerがインストールされている必要がありますのでiPhone等では使えません。
JSONPを使う
JSON(JavaScript Object Notation)は、JavaScriptのオブジェクト用の表記をしたデータ形式です。JavaScriptからは最も利用しやすいデータ形式で、この形式をコールバック関数の中にいれた形がJSONP(JSON with Padding)です。

現状ではJSONPを使う方法がJavaScriptでXMLHttpRequestのAJAX通信をクロスドメイン間で行うにあたり、一番良く使用される方法ではないかと思いまが、もうひとつPostMessageを使ったクロスドメイン通信の方法がありますので、紹介させていただきます。

PostMessageは安全にクロスドメイン通信を可能にするためのメソッドです。別のウィンドウやIFrameに表示された別ドメインのJavaScriptに対して、通信することができます。この機能を利用して、APIと同一ドメインにAPIにアクセスするための送受信用のHTMLファイルを設置してIFrame内に表示することで、PostMessage経由でクロスドメインを突破する方法です。

20130528_img

通信元ファイル(APIとは別ドメイン)

/**
 * メッセージ受信
 */
$(window).on("message", function (e) {
    var data = e.originalEvent.data;
});

/**
 * メッセージ送信
 */
$("#btnSend").on("click", function () {
    receiver.postMessage({
        url: "http://knockknock.jp/www/sample/20130528/addition.php",
        type: "post",
        data: {},
        cache: false,
        dataType: "json"
    }, "http://knockknock.jp");
});
----------<< 中略 >>----------
<iframe name="receiver" id="receiver" src="http://knockknock.jp/www/sample/20130528/receiver.html"></iframe>

通信先ファイル(APIと同一ドメイン)

/**
 * メッセージ受信
 */
$(window).on("message", function (e) {
    var data = e.originalEvent.data;
    var referer = e.originalEvent.origin;
    $.ajax(data).done(function (data, status, xhr) {
                parent.postMessage(data, referer);
            }).fail(function (xhr, status, error) {
                parent.postMessage(null, referer);
            });
});

通信元では、IFrameに対してIDを設定してpostMessageメソッドを実行。第一引数に$.ajaxメソッドの第一引数にわたるオブジェクトを指定、第二引数に通信先のドメインを渡します。通信先では受け取ったオブジェクトを元に$.ajaxメソッドを実行し、APIにアクセスします。帰ってきた結果をparent.postMessageで通信元に返却します。第一引数には返却データを、第二引数には返却先のドメインを指定します。

以下サンプルです。
20130528_img2

この方法で有効な場合は極めて限定的です。
クロスドメイン間の通信である。

  • 通信元にPHP及びCGI等が設置できない。
  • 対象環境にFlashPlayerが入っていないものがある。
    (Flashが使える場合は、crossdomain.xmlを設置してswfで通信することが可能です。)
  • 対象ブラウザにIE8、IE9が含まれている。
    (IE10以降もしくはスマフォの場合はXMLHttpRequest2を使った方がスマートです。)

ここまでではJSONPで対応可能ですが、以下の場合に対応が難しくなってしまいます。

  • HTTPヘッダーにカスタムヘッダー情報を追加して値を引き渡す必要がある。
    (jsonpではHTTPヘッダーにカスタムヘッダを付与できない?)

非常にまれなケースになりますが、このような条件がそろうとpostMessageを利用するという選択肢になるのかもしれません。APIをたたく目的でなくとも純粋にpostMessageは何かと便利なものですので、使う機会はあると思います。

コメント

  1. […] ることができます。以下に具体例を交えている記事がありました。ただ、ブラウザのサポートの問題等もあり、身の回りではあまり流行っていない印象(?) http://www.knockknock.jp/archives/221 […]