前回の記事では、サンプルとして提示されているJavaScriptの内容を確認しました。
dankogai作のAWS APIからJSONの仕組みを理解する(1/2)
今回は前回の内容を踏まえてソースの簡略化を行い、その後JSONPの仕組みを理解して行きます。
コードの簡略化
まずは、前回の解析を元にJSONPの理解と直接関係ない処理を削除してコードをシンプルにしていく。最初は、画面load時の処理と、取得結果列挙の処理変更を行う。load時の処理は不要なので削除し、検索結果は書籍のタイトル表示だけにとどめておく。
この変更で、ソースは以下のように半分(81->40行)まで減らせた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <a id="amazon.link" target="_blank" href=""> <img id="amazon.img" src="" style="float:right; margin-left:1em"> </a> ASIN: <input id="asin" value="4534045220" size="10" type="text"><input onclick="JSONP.get(this.previousSibling.value)" type="submit"> <div id="amazon.attr"></div> <script> (function(d){ var $ = function(id){ return d.getElementById(id) }; var json2list = function(json){ var spanNode = d.createElement('span'); var textNode = d.createTextNode( json.Title); spanNode.appendChild( textNode ); return textNode; }; JSONP = { get:function(asin){ var u = 'http://api.dan.co.jp/asin/' + asin + '/JSONP.run'; var s = d.createElement('script'); s.charset = 'UTF-8'; s.id = s.src = u; d.body.appendChild(s); }, run:function(json){ $('amazon.attr').innerHTML = ''; if (json.Error){ $('amazon.attr').appendChild(json2list(json)); return; } $('amazon.link').href = 'http://www.amazon.co.jp/gp/product/' + json.ASIN + '?ie=UTF8&linkCode=as2tag=blogsofdankog-22'; if (json.ImageSets){ var imageset = json.ImageSets.ImageSet; if (imageset.length) imageset = imageset[0]; $('amazon.img').src = imageset.MediumImage.URL; }else{ $('amazon.img').src = 'http://ec1.images-amazon.com/' + 'images/G/09/nav2/dp/no-image-no-ciu._AA128_.gif'; } $('amazon.attr').appendChild(json2list(json.ItemAttributes)); } }; })(document); </script> |
実行してみるとボタン表示で、問題なく書籍のタイトルが表示された。

さらに、JSONPのハッシュテーブル解除、商品画像表示処理と、スクリプト全体を囲っている大枠の無名関数をカットする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ASIN: <input id="asin" value="4534045220" size="10" type="text"><input onclick="getData(this.previousSibling.value)" type="submit"> <div id="amazon.attr"></div> <script> var $ = function(id){ return document.getElementById(id) }; var json2list = function(json){ var spanNode = document.createElement('span'); var textNode = document.createTextNode( json.Title); spanNode.appendChild( textNode ); return textNode; }; function getData(asin){ var u = 'http://api.dan.co.jp/asin/' + asin + '/displayData'; var s = document.createElement('script'); s.charset = 'UTF-8'; s.id = s.src = u; document.body.appendChild(s); }; function displayData(json){ $('amazon.attr').innerHTML = ''; if (json.Error){ $('amazon.attr').appendChild(json2list(json)); return; } $('amazon.attr').appendChild(json2list(json.ItemAttributes)); }; </script> |
再度、期待通り動くことを確認。

さらに、変数$の削除、json2list関数をdisplayData()に埋め込みを行い、エラー表示も端折ってしまう。
これで、最小限の処理まで削りきった。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ASIN: <input id="asin" value="4534045220" size="10" type="text"><input onclick="getData(this.previousSibling.value)" type="submit"> <div id="result"></div> <script> function getData( asin ) { var s = document.createElement('script'); s.charset = 'UTF-8'; s.src = 'http://api.dan.co.jp/asin/' + asin + '/displayTitle'; document.body.appendChild(s); } function displayData( json ) { nodeParent = document.getElementById( 'result' ); nodeResult = document.createTextNode( "タイトル :" + json.ItemAttributes.Title ); nodeParent.appendChild( nodeResult ); }; </script> |
このソースを元に、JSONPの仕組みを見て行くことにする。
JSONPの動作確認(サーバ側)
まずはボタンをクリックしたときのサーバとのやり取りを、firebugで確認する。
ヘッダをのContent-Typeを確認すると、”application/x-javascript”なので、javascriptのプログラムが送られてきていることが分かる。

次はレスポンスのデータ側。内容は非常に長いけど、注意深く見てみるとJSONデータを引数に持つ関数が1つ定義されているだけということが分かる。

この関数名をサーバがどうやって決めたかというと、最初に作ったリクエストURLの末尾に付与されているので、それをオウム返ししているだけ。(javascriptを見るとのURLに書いてある)
1 | s.src = 'http://api.dan.co.jp/asin/' + asin + '/displayTitle'; |
試しにこれをdisplayTitle2に書き換えると、以下のようにレスポンスで渡される関数名が変更されてきた。
想定どおりの振る舞い。

なので、サーバ側は(JSONの時と比べると)JSONPでは以下の対応を行えば良い事になる。
1.レスポンスヘッダを”application/x-javascript”に変更する
2.レスポンスデータを関数名でくくる。
JSONPの動作確認(クライアント側)
サーバの流れは把握したので、次は、クライアント側のJavaScriptを確認する。まずは、ボタンクリック時のハンドラから。
1 2 3 4 5 6 7 | function getData( asin ) { var s = document.createElement('script'); s.charset = 'UTF-8'; s.src = 'http://api.dan.co.jp/asin/' + asin + '/displayTitle'; document.body.appendChild(s); } </script> |
関数内の処理では,htmlにscriptタグを挿入している。
実際にボタンのクリック前後でhtmlを比較してみると確かにscriptタグが追加されていて、先ほどサーバより取得した関数になっている。
スクリプトの中を見てみると、関数呼び出しの形式なっている。なのでクライアント側では、サーバからの応答受信をトリガーに、displayData()関数がコールされることになる。

実際に呼ばれる関数の中身は以下の通り。
1 2 3 4 5 6 | function displayData( json ) { nodeParent = document.getElementById( 'result' ); nodeResult = document.createTextNode( "タイトル :" + json.ItemAttributes.Title ); nodeParent.appendChild( nodeResult ); }; </script> |
この処理自体は、普通のjavascriptで、idがresultのタグを持つ親ノードの末尾にテキストを追加している。関数の引数は、前述のサーバ側処理確認時に把握したように、サーバが指定している。
上記の仕組みによって、結果的にボタンのクリックをすると画面に書籍名が表示される流れが出来上がった。
まとめ
JSONPは、以下の仕組みで実現されていることが分かった。1.クライアントはHTML上にscriptタグのノードを新しく作る。この際コールバックの関数名も渡す。
2.スクリプトの内容は、サーバから取得する。
3.サーバは、リクエストに対して、データを動的に取得する。
データを取得したら、クライアントへ指定されたコールバック関数呼び出しを行うスクリプトを返す。
4.クライアント側は応答受信をトリガに、コールバック関数を実行する。
(この際、引数にJSONデータがセットされている)
追記:今回学んだことを元に、JSONPのAPIを作成しました。
→ ヤマトの伝票情報取得APIがJSONP対応しました
関連記事
コメントを残す