ぱろっと・すたじお

技術メモなどをまったりと / my site : http://parrot-studio.com/

JSONPって何?(jQueryで簡単にJSONP)

結論から言うと、jQueryを使えばJSONPを扱うのは簡単でです

var uri = "http://[APIURI]?[callback指定パラメータ名]=?"
$.getJSON(uri, params, function(data){
  // data処理
})

しかし、このメソッドが何を簡略化しているのか知らなければ、
jQueryが使えない場合に応用できません


そこで今回は、自分がJSONPの必要性に気付いたところから、
その仕組みを理解していった経過を、メモに残しておきます...φ(・ω・`)


関連:群馬Web勉強会(on 2010/08/21)まとめ - どっかのBlogの前置きのような

Lv0:発端


TokyoCabinetにデータを格納した検索コアAPIと、
Railsのフロントを連動させて、データ検索システムを作る、
というのが今の私の仕事です

Browser -> Rails -> APIProxy(HTTPRequest) -> API(JSON)

なので、勉強会ではその延長で、RDGC+API+jQueryの話をしようと、
(iOSアプリがよくやるような)ローカルHTMLからサーバAPIを叩くデモを作ったのですが、
当然のように全く動きませんΣ(・ω・ノ)ノ


というのも、Ajaxで使われるXMLHttpRequestは、
セキュリティポリシーにより、別ドメインへのリクエストが送信できないのです


仕事のシステムでは送信先がRails=同じサーバで、
内部的にリクエストを処理しているからOKなのですが、
デモはHTMLから直にサーバAPIを叩こうとするのでNGなわけです


これをうまく処理してくれるのが「JSONP」という仕組み、
ということになります

Lv1:静的に対処する


先ほどの問題はあくまでXMLHttpRequestの仕様であって、
Javascript自体は別ドメインにあっても構いません


実際、MSやGoogleが提供するjQueryライブラリを、
ローカルHTMLのscriptタグに書いても動作します


そこで、サーバAPIが返すものを「JSON」から「JavaSscriptの断片」に変更します

// 修正前
{name: parrot, lv: 10}

// 修正後
hoge({name: parrot, lv: 10})

後者はfunctionをJSONで呼び出しているコードです


ということは、あらかじめそのfunctionを静的に書いておけば、
渡されたデータを受け取って処理できます

<script type="text/javascript">
  function hoge(data){
    // dataの処理
  }
</script>
<script type="text/javascript" src="http://someserver/api?name=parrot" />

ただし、これだとfunctionの名称が固定になってしまい、
すでにあるfunction名と被る危険性があるので、
API側でfunction名を指定できるのが一般的なようです

<script type="text/javascript"
 src="http://someserver/api?callback=hoge&name=parrot" />
Lv2:動的に対処する


Lv1のやり方だと、あらかじめ決まったAPIパラメータしか渡せません
つまり、buttonをトリガーにして、その時の入力パラメータを元に、
動的にAPIを叩く、ということができないのです


Blogパーツ等で、最近のデータ5件を取ってくる、
なんてケースにはLv1の方法で十分なのですが、
通常は何らかの動的処理が入るでしょう


そこで・・・

scriptタグでしか外部APIが呼べないなら、
scriptタグそのものを動的生成すればいいじゃなーい(`・ω・´)

・・・と考えた人がいたわけです


つまり、button等をトリガーにして、
動的にscriptタグを生成し、HTMLのDOMにくっつけることで、
半ば無理矢理ながら動的に処理しよう・・・と


しかし、これだけだと例えばbuttonが押されるたびにscriptタグが増えてしまうので、
実際は不要になったところで、タグをDOMから削除する処理も必要になります

Lv3:とりあえずまとめてみる
  1. APIのJSONを処理したいfunctionを先に用意
  2. APIのURIにfunction名パラメータを含める
  3. トリガーによって動的に前項をsrcに指定したscriptタグを生成
  4. scriptタグをHTMLのDOMにappend
  5. 戻ってきたデータを処理
  6. scriptタグを削除


文章で書くとこれだけですが、
実際に実装するとなるとかなり面倒ですよね・・・(´・ω・`)

Lv4:jQueryを使う


こういった「面倒な処理」を全部面倒見てくれるのが、
冒頭のgetJSONメソッド、ということになります
原理的にはここまで書いてきた物そのものです

var uri = "http://[APIURI]?[callback指定パラメータ名]=?"
$.getJSON(uri, params, function(data){
  // data処理
})

URIのcallback指定パラメータ名に「?」を指定することで、
jQueryが動的にfunction名を生成し、一連の処理をした後、
scriptタグの削除までやってくれます


また、呼び出し先が同じドメインであった場合は、
自動で通常の呼び出しに置き換えて処理してくれるようで、
非常に助かります(`・ω・´) b

Lv5:セキュリティ的な問題


APIを実装する側は、引数をきっちり検証しないと、
いろいろな脆弱性を生む可能性があります

  • サーバにダメージを与えるような引数を与える
  • 戻り値のコードがブラウザで実行された時に何かが起こる


発表会で使ったAPIはこれらの検証を行ってないため、
主に後者の問題が発生するはずです
(不正スクリプトを実行する文字列をcallbackに与えると・・・)


これを防ぐため、function名で使える文字列や長さを制限し、
サーバ側できっちり検証することが重要です*1


一方、JSONPを利用する側としては、
サーバが「想定した通りの戻り値」を返すうちはいいですが、
ある日突然、何らかの不正なコードが返される可能性もあります


そもそもJSONPはある種のセキュリティを回避する手法なのですから、
これらのリスクを認識したうえで、必要な対策を講じておくことが必要でしょうね

*1:例:はてなブックマークAPI http://b.hatena.ne.jp/help/api