JSONを出力するプログラム(json.php)はできましたので、次はそのデータにアクセスし、データを取得・表示する受信側を作成します(ajax.php)。これも『PHP逆引きレシピ 第2版』からの引用です。なお、サーバ環境に関しては『PHP逆引きレシピ 第2版』のままとしていますので、そちらを参照願います。良い本ですよ。
名称は ajax.php ですが、下記の様に PHP ではなく HTML + jQuery で構成されています。(7,8行目にコメント追加修正)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>JSON形式のデータを返すWeb APIを作りたい</title>
<link href="../../css/style.css" rel="stylesheet"> <!-- 7行目 CSS 読み込み -->
<script src="../../js/jquery-2.0.3.min.js"></script> <!-- 8行目 jQuery ライブラリ読み込み -->
<!-- Internet Explorer 6~8で動作させる場合はバージョン1系を使用してください -->
<!-- <script src="../../js/jquery-1.10.2.min.js"></script> -->
<script>
$(function(){
// jQueryを使用して、読込ボタンがクリックされたら処理を行ないます。
$("#load").on("click", function(){
// Web API(json.php)で生成したjsonデータを取得して処理します。
$.getJSON("json.php", function(data){
for (var i in data) {
// 行のオブジェクトを生成します。
var tr = $("<tr>");
// 列のオブジェクトを生成して行に追加します。
var td_item = $("<td>").text(data[i].item);
tr.append(td_item);
var td_price = $("<td>").text(data[i].price);
tr.append(td_price);
var td_orders = $("<td>").text(data[i].orders);
tr.append(td_orders);
// 行のオブジェクトをテーブルに追加します。
$("#listbox").append(tr);
// 読込ボタンを非表示化します。
$("#load").hide();
}
});
});
});
</script>
</head>
<body>
<div>
<input type="button" value="読込" id="load">
<table id="listbox">
<tr>
<th>品名</th>
<th>価格</th>
<th>注文数</th>
</tr>
</table>
</div>
</body>
</html>
http://localhost/php-recipe/07/11/ajax.php を実行すると、下記ページが表示されます。
「読込」ボタンをクリックすると、
と表示され、json.php で定義した JSONデータが ajax.php によって受信、表示されました。 これは何でもないようなことですが、実は ajax.php から HTTPのGETメソッドを発行し json.php を呼出し、JSONデータ(Content-Type: Application/json)の受信、Unicodeデコード (json.php でエンコードされています) 、javascriptオブジェクト(XMLHttpRequest)化を行っています。そして、そのオブジェクトにアクセスし、<table>タブに挿入することで、画面遷移なしにデータの表示を行っているのです。jQuery の $.getJSON メソッド便利です。
HTTP/GETメソッドを発行しているということは、例えば ajax.php の16行目のjQuery の表記を下記のように JSONP で取得する様に変更すれば、リモートサーバの JSON データを取得することも可能です(かなりトリッキーですし、jQuery 側のクロスドメイン制限、セキュリティ上の留意点など注意すべき点がありますが)。
$.getJSON("http://リモートサーバ/~/json.php?callback=?", function(data){
* すいません上記の「読込」後のボタンですが本来は表記されません
8行目で jQueryライブラリを読み込んでいるのですが、CDNという機能を使用すると、jQuery をWebサーバ上に置くことなく手軽に利用できます。(その他にもメリットがあります)
最新の jQuery を読み込むように8行目を下記のように書き換えます。
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
メリットとしては、
が挙げられます。
良いことだらけの CDN に見えますが、デメリットもあります。アクセス先のサーバがダウンしたり、ネットワークに異常が発生した場合 jQuery のライブラリが取得できない場合が発生します(私も1回経験しました)。そこで実運用環境では、下記の様に自サーバ上にも jQuery の環境を整備し、CDNが使用できない場合(window.jQuery が undefined となり、|| 以降が実行される)バックアップ機能を持たせるのが良いのかなあと。3番目のメリットが無くなりますが。
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.min.js"></script> <script>window.jQuery || document.write('<script src="../../js/jquery-2.0.3.min.js"><\/script>')</script>
それでは次回まとめに入ります。
JSONPによるリモートサーバからの JSON データの取得ですが、一般に公開されている Web API のアクセスに関しては思いの外、大変です。http://weather.livedoor.com/weather_hacks/webservice で公開されている、livedoorの「お天気Webサービス」の情報を上記方法で取得しようとしてもエラーとなります。
Chomeのデバッガを使用しながら、金沢の情報を取得してみるとデータの取得はできています(下記引用参照)。しかし、「XMLHttpRequest cannot load http://weather.livedoor.com/forecast/webservice/json/v1?city=170010. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.」というエラーが発生しています。
これは、サーバ側で「Access-Control-Allow-Origin」ヘッダを送信していないため、データの取得はしなかった、というエラーです(エラーというか javascript の仕様)。$.getJSON()では、データは XMLHttpRequest オブジェクトで取得されるのですが、これはクロスドメインではアクセスは許されません。つまり同じドメインのみデータのアクセスが可能なわけです。そのためブラウザ(今回はChrome)は、別のドメインから取得した JSON データの取得を拒否したということです。
回避方法は、いくつかあるようですが、今回の場合は ajax.php と同一ドメインのサーバ上で、 $.getJSON() を実行するサーバ上に「お天気Webサービス」の JSON データを取得する javacsript 以外のプログラムを別途設置するのが実現的かなと考えます。「お天気サービス」では、JSONPの「コールバック」による取得はできないようですし。
もちろん、セキュアな情報を扱う Web API を構築するのであれば、同一ドメインで管理すべきでしょう(認証、セッション管理を別途行うのはもちろん)。
Ajax が出力する「X-Requested-With」ヘッダは、クロスドメイン環境では出力されないようです(私が確認したのは Chrome 32.0.1700.107 m)。そのため、JSONハイジャックを防止する目的で、同ヘッダをチェックする方法は採れないようです。
まとめると
(1) JSONP を使用する ⇒
サーバ側の「コールバック名」を指定すれば JSON データを取得できる
サーバ側で「Access-Control-Allow-Origin: *」の設定も不要
サーバとクライアント側で、「コールバック名」を一致させる必要がある
セキュリティに十分留意する必要がある
サーバ側で「Access-Control-Allow-Origin: *」の設定が必要(ブラウザではデータの受信は行うが、javascript ライブラリでブロックする)
クロスドメイン環境では、X-Requested-With ヘッダが送信されないので、サーバ側での、Ajax からのアクセスチェックには使用できない。同じドメインでのみ使用可能
一部修正しました。まだ試行錯誤中です。間違っていたら追記します。