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

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

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

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

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


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

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

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

次に、index.htmlのjQueryから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を使わなくてよくなるよ〜

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

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