JSONPを使ってみた

JSONPとは、JSON with Paddingと言うことらしいですが、詳しい説明はググってください。

今回やりたいことは、別サーバにあるCSVファイルからパラメータで指定したidにマッチする値を取り出して表示する、というものです。
サーバ側のプログラムはPHPで作ってみました(経験値少ないので変なところがあるかも)。

元データになるuser.csvファイルはこんな感じです。

1,山田太郎
2,鈴木一郎
3,池田次郎

フィールドはidとnameになります。

次にサーバ側の処理です。
と、その前にPHP版のJSONライブラリと、JSONPで呼び出すためのJavaScriptライブラリを用意しておきます。

サーバ側のindex.phpはこんな感じになります。UTF-8で保存しています。

<?php
// JSON.phpを読み込む
require_once('JSON.php');

// utf-8を使えるようにするおまじない
setlocale(LC_ALL, 'ja_JP.UTF-8');

// callback関数名を取得
$cb = $_GET['callback'];
// idを取得
$id = $_GET['id'];

// CSVファイルをオープン
$fp = fopen("user.csv", "r");
$user = array('name' => '');
while (($data = fgetcsv($fp, 1000, ",")) !== FALSE) {
  // $idにマッチする値を見つけた!
  if ($id == $data[0]) {
    $user = array('id' => $data[0], 'name' => $data[1]);
    break;
  }
}
// CSVファイルをクローズ
fclose($fp);
// Services_JSONオブジェクトを作成
$json = new Services_JSON;
// レスポンスヘッダにContent-Typeを設定
header("Content-Type: text/javascript; charset=utf-8");
// JSON形式に変換して、callbackメソッドの引数として返す
echo "$cb(" . $json->encode($user) . ")";
?>

サーバ側のURLは、「http://example.com/index.php」とすると、次のようなURLをブラウザで開くことで、内容の確認が出来ます。
http://example.com/index.php?id=1&callback=hello

このレスポンスは、次のようになります。

hello({"id":"1","name":"\u5c71\u7530\u592a\u90ce"})

レスポンスの先頭の「hello」は、パラメータのcallbackで指定した文字が入ります。
例えば、http://example.com/index.php?id=1&callback=hogehogeで呼び出すと、次のようなレスポンスが返ってきます。

hogehgoe({"id":"1","name":"\u5c71\u7530\u592a\u90ce"})

という感じでブラウザで確認できたら、クライアント側のHTMLを書いてみましょう。

こんな感じです。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<span>JSONPを使うよ</span>
<script type="text/javascript" src="http://example.com/jsr_class.js"> </script>
<script type="text/javascript">
// callbackで呼び出される関数
function hello(data) {
    var resultData = '';
    resultData += '<p>こんにちは ' + data.name + ' さん</p>';
    document.getElementById('hellotarget').innerHTML = resultData;
    bObj.removeScriptTag(); 
}
var req  = 'http://example.com/index.php?id=1&callback=hello';
var bObj = new JSONscriptRequest(req); 
bObj.buildScriptTag(); 
bObj.addScriptTag();
</script>
<div id="hellotarget" style="width:180px; border:thin #666666 solid;"></div>
</body>
</html>

JSONscriptRequestの引数で、サーバ側のスクリプトを呼び出します。
このレスポンスは、callbackで指定したJavaScriptの関数に対して、引数を設定した命令文として返ってきます。
この例では"hello"という関数を指定しているので、そのままfunction hello(data)が呼び出されます。
helloの中では、受け取った連想配列のnameの値を

<div id="hellotarget">

に挿入しています。

ただ、何も考えずに作るとCSRFの餌食になってしまうので、基本的には公開しても良いデータだけを扱うようにするべきでしょう。

さて、今回こんなことを調べる気になったのは、知人からの問い合わせでした。
その経緯はここでは書けませんが、良い勉強になったことは確かです。