Hatena::ブログ(Diary)

Back yard : yuya_lush’s report このページをアンテナに追加 Twitter

2011-06-28

Ajaxのクロスサイト通信をJSONPを使わないでやってみよう

jQueryなどを使いAjaxで通信などをしていると、他のサーバから直接JSONが取れたら便利なのにと思ったりします。

通常はこういったときはJSONPなどを使うのですが、もっと楽な方法は無いものかと探していたところ「Access-Control-Allow-OriginなるものをHTTPヘッダーに入れればできる!」という記事があったのを思い出し、夜中にフツフツと試してみました。

先に結論を書いておくと、冒頭の例え「他サーバから直接JSONを取る」には、他のサーバーが返してくるResponseのヘッダー部分に「Access-Control-Allow-Origin:"*"」と入っていると、受け取ったブラウザJSONを処理してくれます。

このテクニックは特殊なテクニックではなく、いたって正攻法とのことでした。そのため、ほとんどのブラウザで同じく処理されます。複数のWebサーバのヘッダを調整することができるのであれば、活用することでとても楽にAjaxのクロスサイト通信を実装することができます。企業内システムでJSONPを使ってデータの取得をしているような場面では、充分使えるテクニックなので、ぜひ活用してみてください。

それでは、Gistに公開したソースとCacooで書いた図を元に、何をどうすれば良いのかを説明してみます。


ということで、まずは図をご覧下さい。

f:id:yuya_lush:20110629012728p:image

ブラウザでServer1にアクセスし、index.htmlを取得します。中にはjQueryで$.getJSONを呼び出すJavaScriptが書かれています。

最初はindex.htmlと同じサーバからjsonを取得します。この時はgetを使っています。もちろんindex.htmlと同じサーバ(同一ドメイン)であるので、正常にリクエストが飛び、リプライが返ってきてjQueryが受け取った結果を処理してくれます。

次に、index.htmljQueryからServer2にリクエストを飛ばします。この時、てっきりリクエスト自体が飛ばないのかと思ってたのですが、どうやらリクエストは飛ぶようです。そして、Server2はリクエストに従い、index.jsonとしてJSONを返信します。(もちろんJSONなのでJSONPとして処理するために必須のcallback関数などは入ってません)

ブラウザはServer2からのリプライを受け取ると、HTTPヘッダ(ResponseのHTTPヘッダ)を確認します。そしてそこに「Access-Control-Allow-Origin:"*"」と書かれていると、ブラウザJavascriptとして処理を行います。結果、JSONとして受け取った内容が表示されることになります。

私はこの「Access-Control-Allow-Origin」は、てっきりServer1から送られるindexhtmlの時点で入っていないとできないものだと思ってましたが、正解はServer2の方でした。

さて、今回はこの構成を2台のPCを使い、Rails3とjQueryを使って環境を作ってみましたWebサーバWebrickを使ったお手軽環境です。(なぜか、色々と調べてみるとPHPでのサンプルはあったのですがRailsがありませんでした。また、jQueryを使わず、XMLなんちゃら!と正しく書いている情報ばかり。)

以下、Gistに公開してるソースを貼り付けておきます。

まずはServer1のindex.htmlとtest.jsonを処理するコントローラー

次にindex.htmlとしてブラウザに送られるView

index.htmlから取得される同一ドメインJSONのView

次に、クロスドメイン先にあたるServer2のJSONを返すコントローラー

Server2から返されるJSONのView

そして最後が、今回一番重要となるHTTPヘッダを調整するコントローラー

なお、本当にアクセスできるようになるか試すため、最後のapplication_controllerの

before_filter :allow_cross_domain_access

# before_filter :allow_cross_domain_access

と最初はしておくと良いと思います。そして、その後コメントアウトを外す感じです。

あとRails3では特に気にしなくてもいいのですがRails2を使って、この実験をする場合はroutes.rbにて

map.connect ':controller/:action.:format'

などとしないとエラーになりますので、ご注意を。デフォでは

map.connect ':controller/:action/:id.:format'

になってます。

この実験のポイントは

  1. Chromeを使い、ディベロッパーツールを使いましょう。
  2. FirefoxFirebugでも良いと思います。
  3. 見るべきところは「Network」で通信が発生しているか
  4. さらに「Console」を見ることで、処理されない/処理されたを確認できます

なお、これはあくまでXHRのクロス通信だけを目的としています。調べたところでは、Cookieを両方のドメインで共有するなどをしたい場合は「Access-Control-Allow-Origin:"*"」とせずに「*」部分に正しく相手ドメインを書かないとうまくいかないという記事も有りました。この点については、今回確認していないので軽く書いておく程度にしておきます。

ということで!

JSONを送ってくれる外部のサーバHTTPヘッダに「Access-Control-Allow-Origin:"*"」を入れられるのであれば、JSONPを使わなくてよくなるよ〜

という結論となりました。

よし。これで寝られる。すっきり!

mat_akimat_aki 2011/06/29 09:33 へーーーー、Server1側でやるのかと僕も思ってました。

Server2側がAllowしたときのリスクってなんでしょうね?

yuya_lushyuya_lush 2011/07/01 22:20 ちょっと不安になってもう一度仕様を調べました。(http://www.w3.org/TR/cors/)
まず前提として、サーバのリソースはだれのものかというと、サーバを管理している側のものになります。
リクエストを制限するのではなく、サーバ側はアクセスしてもらいたくないなら認証を設置することで保護できます。
認証がないということは、基本的には誰にでも使ってもらって構わないと言っていることになるかと。
ただ、部分的な人にだけ使ってもらいたいなということをヘッダで主張することができるようになっていて、それをブラウザが受け取り、配信元の指示に従ってユーザに渡すのをブロックしてくれる感じです。
なので、「Server2側がAllowしたときのリスク」というのは、*でAllowするという場合は、どんなドメインからのリクエストであっても、どうぞユーザーにお見せ下さい!とブラウザに指示していることになります。
・・・って感じだとおもいます。たぶん。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証