最近Backbone.jsを使い始めました。
JavaScriptのMVCフレームワークの内、知名度が最も高いらしいということで。
日本語の入門記事としてはこちらが非常に分かりやすく、参考にさせていただいています。
Backbone.js 入門
Model.save()のサンプル
Backbone.jsのModelを使ってデータ登録・更新を行う場合、通常はModelメソッドのsave() を使うと思いますが、save()の動作を確認する簡単なサンプルを作ってみました。
(save()を使うと、リソースAPIに対してPOST(またはPUT)リクエストが送信されます。)
サンプルの動作
- 「送信」ボタンをクリックすると、入力された名前と住所をPOSTリクエストで送信する。
- POST送信された名前と住所のJSON文字列が、ボディに含まれてレスポンスが返ってくる。
- レスポンスされた名前と住所を画面に表示する。
サンプルを構成するファイル
以下、上記サンプルを動作させているファイルの一覧と、ファイルの内容です。
ファイル一覧
- backbone-save-test.html
- 名前と住所を送信する画面
- backbone_save_test.js
- 名前と住所の値をModelに保持して、save(POSTリクエスト)するJavaScript
- backbone_save_test.php
- POSTリクエスト先のPHPファイル
backbone-save-test.htmlの一部
名前と住所の入力部分と、レスポンスデータを表示するテンプレート部分です。
<table class="table table-bordered"> <thead> </thead> <tbody> <tr> <th>名前</th> <td><input type="text" id="user_name"></td> </tr> <tr> <th>住所</th> <td><input type="text" id="address"></td> </tr> </tbody> </table> <input type="button" id="btn_send" value="送信"> <br><br> <!-- テンプレート(post_data_template)を表示する領域 --> <div id="post_data_area"></div> <!-- /テンプレート(post_data_template)を表示する領域 --> <!-- レスポンスを受け取って表示するテンプレート --> <script type="text/template" id="post_data_template"> <table class="table table-bordered"> <caption>POSTされたデータ</caption> <thead> <tr><th>名前</th><th>住所</th></tr> </thead> <tbody> <tr> <td><%= user_name %></td> <td><%= address %></td> </tr> </tbody> </table> </script> <!-- /レスポンスを受け取って表示するテンプレート -->
backbone_save_test.js
名前と住所の値をModelに保持して、save(POSTリクエスト)します。
$(function() { var User = Backbone.Model.extend({ // ModelのリソースURI(本サンプルでは、Model.save()によるPOSTリクエスト先) urlRoot : './backbone_save_test.php', defaults : function() { return { 'user_name' : null, 'address' : null }; } }); var BtnSend = Backbone.View.extend({ el : $('#btn_send'), model : User, initialize : function(options) { this.model = options.model; }, events: { 'click' : 'click' }, click : function(e) { var reqData = { 'user_name' : $('#user_name').val(), 'address' : $('#address').val() }; // 送信するデータをModelにセット this.model.set(reqData); // save()を呼び出すと、model.urlRootにPOSTリクエストが送信される this.model.save(null, { success : function(model, resp) { // レスポンスを受け取ってalert表示する alert('名前:'+resp.user_name+' 住所:'+resp.address+' を受け付けました。'); // レスポンスを受け取ってテンプレートに表示する $('#post_data_area').html(_.template($('#post_data_template').html(), resp)); }, error : function(model, resp) { alert('エラー:送信に失敗しました。'); return false; } }); } }); var user = new User(); var btnSend = new BtnSend({model: user}); });
backbone_save_test.php(修正前)
Model.save()により送信されたPOSTデータを、$_POSTから取得しようとしています。
<?php $reqData = $_POST; $respBody = array('user_name' => $reqData['user_name'], 'address' => $reqData['address']); echo json_encode($respBody); // end of file
$_POSTでは、application/jsonのPOSTデータを取得できない
Backbone.jsのsave()によるPOSTデータをPHPで取得する際に、少し困ったことがありました。
リクエストをPHPで受け付けた場合、$_POST($_REQUEST)などのスーパーグローバルを使ってリクエストデータを取得すると思いますが、$_POSTにはリクエストデータが入っていないのです。
Backbone.jsはPOSTリクエストボディにJSON文字列をそのまま入れて送信するため、
$_POSTが想定している形式と違うということのようです。
Backbone.jsとPHPが扱うContent-Typeの違い
- Backbone.js のPOSTリクエスト
- Content-Type: application/json
- PHPが想定しているPOSTリクエスト
- Content-Type: application/x-www-form-urlencoded
PHPでBackbone.jsのPOSTデータを取得する方法
方法は二つあります。
- “php://input”からリクエストボディを直接読み込む
- Backbone.emulateJSON = true に設定する
php:// — さまざまな入出力ストリームへのアクセス
php://input は読み込み専用のストリームで、 リクエストの body 部から生のデータを読み込むことができます。 POST リクエストの場合は $HTTP_RAW_POST_DATA よりも php://input を使うのが望ましいでしょう。php.ini ディレクティブの設定に依存しないからです。 さらに、$HTTP_RAW_POST_DATA がデフォルトで設定されない場合は、 always_populate_raw_post_data を有効にするよりも $HTTP_RAW_POST_DATA を使うほうがメモリの消費量が少なくなるでしょう。 php://input は、 enctype=”multipart/form-data” に対しては使用できません。
emulateJSON
If you’re working with a legacy web server that can’t handle requests encoded asapplication/json, setting Backbone.emulateJSON = true; will cause the JSON to be serialized under a model parameter, and the request to be made with aapplication/x-www-form-urlencoded mime type, as if from an HTML form.
上記の通り、Backbone.emulateJSONをtrueに設定する方法だと、Content-Type:application/x-www-form-urlencoded でPOSTリクエストが送信されるようになりますが、
今回は”php://input”を使う方法で対応します。
emulateJSONの説明文中の”a legacy web server”の部分が気に入らないというのもあって(レガシーとかじゃないだろとw)
backbone_save_test.php(修正後)
backbone_save_test.phpの内容を、以下のように修正します。
<?php $reqData = json_decode(file_get_contents('php://input'), TRUE); $respBody = array('user_name' => $reqData['user_name'], 'address' => $reqData['address']); echo json_encode($respBody); // end of file
修正後のサンプル
修正後のサンプルが、冒頭にも紹介したこちらです。(冒頭と同じ内容です)
Backbone.js saveサンプル
最近のコメント