jQueryの”.getJSON”で”200 OK”のレスポンスにデータがない

この記事は次のブログに引っ越しました。

Google Gadgetで、jQueryの".getJSON"メソッドを使って別ドメインから(クロスドメインで)、JSONデータが受け取れない際に注意すること。 HTTPリクエスト・ヘッダに"Origin"が渡されていたら、レスポンス・ヘッダに"Access-Control-Allow-Origin"が必要 ※jQuery+IE8以上の場合は、リクエスト側にも工夫が必要です。 JSONを吐き出すサーバ側 PHPならこんな感じ↓ ■簡易版 ?php $aResult = array( 'hoge' = 'this is a returned msg via ajax.' ); header( "Content-type: application/json; charset=utf-8\n\n"); //なんでもバッチコイ設定 header( "Access-Control-Allow-Origin: *\n\n"); echo json_encode($aResult); ? ■ちょっと安心?版 ?php $aResult = array( 'hoge' = 'this is a returned msg via ajax.' ); header( "Content-type: application/json; charset=utf-8\n\n"); //リクエスト・ヘッダにOriginがあれば、その値を使う $sOrigin = ( Empty($_SERVER['HTTP_ORIGIN']) ) ? '*' : $_SERVER['HTTP_ORIGIN']; header( "Access-Control-Allow-Origin: {$sOrigin}\n\n"); echo json_encode($aResult); ? jQueryの"getJSON"メソッドなどを使ってAJAXな通信をする際、リクエスト側がHTTPヘッダに"Origin"というパラメーターを渡してくる事があります。 ■リクエストヘッダ Host www.your-server.com User-Agent Mozilla/5.0 (Windows NT 6.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1 Accept application/json, text/javascript, */*; q=0.01 Accept-Language ja,en-us;q=0.7,en;q=0.3 Accept-Encoding gzip, deflate Accept-Charset Shift_JIS,utf-8;q=0.7,*;q=0.7 Connection keep-alive Origin https://xxx-a-sites-opensocial.googleusercontent.com ←これ これは、Firefox3.5以上,IE8以降から取り入れられた、CORS(Cross-Origin Resource Sharing)と呼ばれるクロスサイト(別ドメイン)間で直接データのやり取りをする際の1つのセキュリティ対策のようです。 つまり、クライアント側のリクエスト・ヘッダーの"Origin"の値と、サーバ側からのレスポンス・ヘッダーの"Access-Control-Allow-Origin"の値が有効でなければ、ブラウザがレスポンス結果を破棄するという仕様です。トークン的なものですな。詳しくは後述。文末にヘッダのサンプルも記載してあります。 また、上記の例では、許可する"Origin"を「すべて」、つまり"*"(アスタリスク)を指定しているので、「この結果は誰でも許可するよん」ということです。T.P.O.で、クライアントを限定してもいいでしょう。 ・Firefoxでの仕様説明ページ 「HTTP access control」 ・MicrosoftのIE8での仕様説明ページ「ドメイン間要求 (XDR) の概要」 ・今回の情報元のページ「Access-Control-Allow-Origin と Access-Control-Allow-Headers について」 ・動作について詳しい説明のページ「Ajax - Goodbye, JSONP. Hello, Access-Control-Allow-Origin」 ・IEの動作で悩んだら助かったページ「IE8+jQueryによるクロスドメイン通信とXDomainRequestラッパーの作成」 詳細と所感 GoogleガジェットをGoogleSitesに載せたのですが、jQuery1.7のgetJSONでデータが取得できなくなりました。 IE8, Firefox7, Chrome15のいずれも同じ現象です。 そこで、動きをトレース(追求)するためにFirefoxfirebugを使い、コンソール・タブでGETによるリクエスト・ヘッダーの内容を見ると赤くエラー(バッテン・マーク)が出てました。 こいつかーい。 エラーが出てるなら原因は追求できそうと、一瞬安心したのですが、なんとエラー番号を見ると"200 OK"と、サーバからはバッチグーなレスポンスを受けているようでした。 「じゃぁ、何でエラーやねん」と、このエラーの頭にある「+」をクリックして詳細を見てみると、レスポンスが空でやんした。 つまり、リクエストも正常で、サーバからもレスポンスはあった(200でおk)だったけど、データ無ッシングということです。 どうやら他のサイトのJSONを読も込ませても同じなので、サーバからデータが届いてからに原因がありそうである。 まさか、セキュリティ・ソフト系がブロックしてる系?ということで、一瞬外してみたもののNG。 よくよくHTTPヘッダーを見てみると、"Origin"という見慣れないヘッダーがあった。 調べてみたら、なんと、最近のおしゃれでモダンなブラウザに実装されているセキュリティ方法とのこと。なんじゃらほい。 もともと、Javascriptでは"Same-Origin"ポリシーと言って、クロスドメイン間の通信は基本的に出来ないので、リクエスト側の同一ドメイン上にあるPHPなどでデータを読み込んで来てから、Javascriptに渡すといった、プロクシ的(抜け穴的)な手法が取られていました。(下図参照) マッシュアップも死語になりつつありますが、クロスドメイン間のデータ通信が重要になってきたので、無駄なパケットを減らす意味からも、W3Cのすごい人たちが「ぶっちゃけ、安全になら直接やりとりしちゃって良いんじゃね?」と下図のように動作を変えようというムーブメントらしいです。 直接といっても、信頼できるサーバ間でないと危険なので、リクエストする側とレスポンスする側で、"Origin"を通して信頼しあおうということのようです。 開発する側から見ても、プロクシを通さなくても良いぶん、かなり勝手が良くなる仕様でしょう。 Firefox, Chrome, Safariでは"XMLHttpRequest"を拡張して"XMLHttpRequest Level 2"という手法で実装しようとしていて、IE8以降では" XDomainRequest"という新しいAPIで実装しようとしています。 IEで"Access-Control-Allow-Origin"が有効にならない XDR(クロスドメイン間の通信)において、FirefoxChromeは上記ヘッダを追加することで解決できたのですが、IE8が動かない。 どうしたものか。わかり次第、ここに追記することにしよう。 関連資料 ■リクエストヘッダ Host www.your-server.com User-Agent Mozilla/5.0 (Windows NT 6.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1 Accept application/json, text/javascript, */*; q=0.01 Accept-Language ja,en-us;q=0.7,en;q=0.3 Accept-Encoding gzip, deflate Accept-Charset Shift_JIS,utf-8;q=0.7,*;q=0.7 Connection keep-alive Origin https://xxx-a-sites-opensocial.googleusercontent.com ←これがあったら ■レスポンスヘッダ Date Sat, 12 Nov 2011 06:29:36 GMT Server Apache X-Powered-By PHP/5.2.17 Access-Control-Allow-Origin * ← これが必要 Content-Type application/json; charset=utf-8 Keep-Alive timeout=5, max=100 Connection Keep-Alive Transfer-Encoding chunked