Edited at

沈思黙考:jsonpの仕組み

More than 3 years have passed since last update.


jsonpはなぜ必要か

最近のWEBアプリケーションは、サーバーでHTMLを生成する方法ではなく、サーバーは要求されたデータをjsonで返し、クライアントがajaxを用いてそのjsonデータを受け取りにいき画面に反映する、という手法をとる。

ajaxを行う際、最初のHTMLを生成したサーバーと同じサーバーに通信を行うのであれば問題はないが、異なるサーバーにデータを取りにいこうとすると、javascriptのセキュリティの制限を受けて、通信をすることができない。

そこでjsonpという方法が登場し、この問題を解決する。


jsonpの動作原理

HTMLでは、<script>タグで読み込むjsは、最初のHTMLを生成したサーバーと異なっていてもよい。そして、srcに指定するのは、js以外のURLでもかまわない。

この性質を使って、別サーバーのURLを呼び出すことにする。

ここでは、当然、jsonを返すURLを指定する。たとえば、phpによるWEBアプリケーションを示してみる。(http://domain/json.phpとしよう)


Fig1.サーバー(json.php)のソースコード

<?php

$RESPONSE['data1']='val1';
$RESPONSE['data2']='val2';
header('Content-Type: text/javascript; charset=utf-8');
echo sprintf("xcallback(%s);",json_encode($RESPONSE));
?>

このプログラム(http://domain/json.php)を呼び出すと、


Fig2.json.phpをブラウザから呼び出した結果出力

xcallback({"data1":"val1","data2":"val2"});


という出力を得る。

これを


Fig3.HTMLからjson.phpを呼び出す

<script type="text/javascript" src="http://domain/json.php"></script>


としてHTMLから呼び出すと、Fig2の出力は、javascriptとして認識される。

もし、xcallback(json)という関数が存在すれば、それが呼び出される。なければエラーになる。xcallback(json)を定義して、HTMLにデータを書き出すようにしてみよう。


Fig4.

<html>

<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script>
function xcallback(json) {
$('#json').text('json:'+JSON.stringify(json));
}
</script>
</head>
<body>
<div id="json"></div>
</script><script type="text/javascript" src="http://domain/json.php"></script>
</body>
</html>

このhtmlファイルは、domainと異なるサーバーにおいて実行することができる。なんならローカルファイルとしてブラウザに読み込んでも問題なく実行できる。

なお、xcallbackを呼び出しは、すべての準備が整ってからでなければならないので、json.phpのローディングが最後になるように配置した。

json.phpを呼び出した結果、xcallbackが呼び出される、という動きはこれで理解できたと思う。しかし、このままでは、任意の時点でサーバーにデータを要求できるとは思えない。json.phpの呼び出しを動的に行うように変更してみよう。これはjQueryを使うなら簡単だ。$.getScript()でスクリプトを追加できる。


Fig5.

<html>

<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script>
function xcallback(json) {
$('#json').text('json:'+JSON.stringify(json));
}
$(document).ready(function(){
$.getScript("http://domain/json.php");
});
</script>
</head>
<body>
<div id="json"></div>
</body>
</html>

以上の説明でjsonpの動作原理がわかるだろう。$.getScript()を呼び出せば、その時点でのjson.phpが返す値がxcallback関数にわたり処理される、ということも容易に想像できるはずだ。

ただ、Fig5では、固定のjsonが返ってくるだけのサンプルなので、ありがたみがいまひとつ感じられないだろう。データのやり取りを動的に行う方法を次にしめしたいと思う。


jQuery-jsonp.jsを使った実用的なjsonp呼出し

jQueryの$.ajaxで、jsonpを利用することもできるが、ファイルがないとか、なんらかの事情でエラーが発生した場合に、正しくエラーハンドリングを行うことができない。エラーが発生するとだんまりになるのだ。

そこで、ここでは、jQuery-jsonp.jsの$.jsonpを使う方法を示そう。

$.jsonpを使うと、動作原理で示した方法を内部で実行してあたかも$.ajaxのような振る舞いでjsonpの呼び出しを行ってくれるのだ。

ここでは、サンプルといえども実用的であるために、クライアントからデータ(json)を送信し、それをphpで受け取り、そのままjsonで返すという仕組みにすることにした。

まずは、クライアントのプログラムから示す。


Fig6.クライアントプログラム

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>

<script type="text/javascript" src="http://cloud.github.com/downloads/jaubourg/jquery-jsonp/jquery.jsonp-2.4.0.js"></script>

<script>

$(document).ready(function(){
$.jsonp({
cache: false,
url: 'http://domain/json.php',
data: {"postIntData1" : 2,"postStringData2" : "test2"},
callback: 'xcallback',
timeout: 10000,
success: function(json, textStatus, xOptions) {
$('#success').text('success:'+JSON.stringify(json));
},
error: function(xOptions, textStatus) {
$('#error').text('error:xOptions='+xOptions+',textStatus='+textStatus);
}
});

});
</script>
</head>
<body>
result<hr>
<div id="success"></div>
<div id="error"></div><hr>
</body>
</html>


$.ajaxの代わりに$.jsonpとしているだけのように見えるほど、$.ajaxと呼出方が似ていることがわかってもらえるだろう。($.ajaxを知っていることが前提になりますが)

data:のところに記載しているものは、クライアントからサーバに送るデータである。今回は、jsonの値をそのまま書いてしまったが、通常は、ここに変数を書くのが普通であろう。


Fig7.サーバープログラム

<?php

if(!empty($_POST)) {
$METHOD='POST'; //jsonpではPOSTメソッドで呼び出されることはない
$REQ=$_POST;
} elseif(!empty($_GET)) {
$METHOD='GET';
$REQ=$_GET;
}

$RESPONSE['HOST']=$_SERVER['SERVER_NAME'];
$RESPONSE['METHOD']=$METHOD;
$RESPONSE['DATA']=$REQ;

header('Content-Type: text/javascript; charset=utf-8');
echo sprintf("xcallback(%s);",json_encode($RESPONSE));


サーバープログラムは呼び出された際のデータをオウム返ししているだけであるが、このようなプログラムを使うと、実際にどのようなデータが渡ってきているのが良く理解できるので、開発を行う前に用意しておくとよいものの1つであると思う。

動作原理でもわかるように、jsonpの呼び出しは'GET'メソッドだけである。'POST'されることはないのだが、このプログラムを使うそれが良く理解できるはずだ。(試しに$.ajaxでPOST呼出してみると良い)