もうそろそろJSONPとはお別れできるのではないかと思い立ったので。
XMLHttpRequestとその問題
AjaxといえばXHRの愛称で親しまれているXMLHttpRequestですが、これには一つ重大な欠点がありました。
これを発行するDHTMLページのドメインが、Request先のドメインと一致する必要があったのです。いわゆる Same Origin Policy というやつです。おかげでサイトをまたがって使えなかったのです。これではマッシュアップできない。どうしよう。
JSONPとその問題
そこで生まれたのが、JSONPという手法です。
これは、script
ノードを追加した時に、単にノードを追加するのではなくsrc
アトリビュートで指定されたスクリプトが実行されるというブラウザーの仕様を利用したもので、こんな感じで動きます。
loadJS = function(url){ var script = document.createElement('script'); script.charset = 'UTF-8'; script.src = url; document.lastChild.appendChild(script); }; if (! window.JSON ) loadJS('http://blog.livedoor.jp/dankogai/js/json2.js'); JSONPcallback = function(json){ alert(JSON.stringify(json)); };
クロスブラザーですしいいことづくめに思えるこの手法ですが、問題が少なくとも二つあります
- コールバック関数名を指定しなければならない
おかげでリクエストURLも長くなりますし、呼び出し方もそれぞれのWebサービスごとに異なりますし、極めつけに、コールバック関数はグローバルスコープで見えるようにしておかなければなりません。無名関数は使えない、少なくともグローバル変数に代入しておかなければならないわけです。
- 入力検証のしようがない
実行は問答無用。レスポンスが期待通り
JSONPcallback({...});
だったらいいのですが、これがlocation.href = 'http://www.example.com/';
だったとしても、ユーザーは指をくわえて見ていることしか出来ません。
この手法は本blogでもさんざん多用してきました。というよりblogであるという本サイトの仕様上、マッシュアップしようとすればそうするしかなかったのです。
Access-Control-Allow-Origin ヘッダー
そこで登場したのが、こちらです。
要はAccess-Control-Allow-Origin
ヘッダーにアクセス元のドメインが入っているか、*
でワイルドカード指定されているかすれば、Same Origin Policy は適用されないよ、というわけです。
IE8でも、XDomainRequest
はこのヘッダーに対応しています。
ということは、以下のような関数を一個用意すれば、クロスブラウザーかつクロスサイトなAjaxが簡単に実現できるというわけです。
getURL = (function(){ var xhr; if (window.XDomainRequest){ xhr = new XDomainRequest(); return function(url, callback){ xhr.onload = function(){ callback(xhr.responseText, xhr.contentType) }; xhr.open('GET', url); xhr.send(); }; } else{ xhr = new XMLHttpRequest(); return function(url, callback){ xhr.onreadystatechange = function(){ if (xhr.readyState === 4) callback(xhr.responseText, xhr.getResponseHeader("Content-Type")); }; xhr.open('GET', url, true); xhr.send(); }; } })();
実際にやってみましょう。
手元で試した結果、Opera 10.61 を除いてうまく行きました。IE8でもFirefox 3.6でもChrome 5でもSafari 5でもiPhoneでもiPadでも。
本来のXHRの実力
これを使うと、こういうことも簡単に出来ます。
fetch = function(){ var url = 'http://api.dan.co.jp/get/' + document.getElementById('url').value; getURL(url, function(content, type){ var node = document.getElementById('got'); if (node.textContent !== undefined) node.textContent = content; else node.innerText = content; }); };
Access-Control-Allow-Origin
で許可さえされていれば、何を取って来てもいいわけです。現時点ではそれほどポピュラーなヘッダーではないので、ここではAccess-Control-Allow-Origin: *
を加えるだけの以下のような簡単なproxyを通していますが、これさえあれば、サーバーの助けをなしに別のWebページを解析したりといった作業が簡単にできるということです。
あと、この方式だと当然JSONも生textのまま受け取るわけですが、nativeなJSON
はIE8を除いたモダンブラウザーは標準装備していますIE8のCompatibility Modeを除けば標準で有効になっていますし、その場合にも上記のように「なければjson2.jsをロードする」のは簡単です。evaる、いやJSONPのようにevaらせる必要はこれでなくなるわけです。
というわけでWeb APIプロバイダー各位におかれては、Access-Control-Allow-Origin: *
をHTTP Responseに加えていただけるとありがたいというお話でした。
Dan the Cross-Site JavaScripter
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。