バカンス駆動開発

この前バカンスって言ったら「古っ」って言われました

backboneからのajaxがpayloadでPHPの$_POSTで取得できない

なんか何言ってるかよくわからないタイトルになっちゃいました。

状況としては、BackboneからサーバーにBackbone::synsを通じてPOST→通常のajaxと同じようにサーバー側(PHP)の$_POSTで取得しようとすると値が空。

chromeのdevelper toolのnetworkタブで送信内容を確認すると普段はFormDataとなっているところが 送信されるものがpayloadとなっていました。どうやらこれが原因のようです。

f:id:egapool:20150728224852p:plain

payloadとは?

payloadとは一般的にhttpリクエストのhttpヘッダーを除いたボディ部分のようです。つまり送信内容そのものということでしょうか。

FormDataとpayloadの違い

どちらもブラウザからデータを送ってることは同じですがContent-Typeとそれに伴いサーバーからのデータの見え方が違うようです。

// FormData
Content-Type: application/x-www-form-urlencoded
foo=bar&name=John

// payload
Content-Type: application/json
{ "foo" : "bar", "name" : "John" }

つまりContent-Typeapplication/x-www-form-urlencodedで送信したものはFormDataとして送信され、application/jsonで送信したものはpayloadとして送信されます。
application/jsonjsonを返す際にかならず設定するコンテンツタイプですね。サーバーからの見え方もJSONそのものです。
http - What's the difference between "Request Payload" vs "Form Data" as seen in Chrome dev tools Network tab - Stack Overflow

payloadを使うとJSONを送信するので、文字列以外にもオブジェクトやブール値など多様なデータを送信できるという利点があります。

違いはわかったので肝心の取得する方法です。やり方は2つあります。

1.サーバー側(PHP)でpayloadを取り扱う方法

$_POSTのかわりに以下の方法で取得できます。

$request_body = file_get_contents('php://input');
$data = json_decode($request_body);

How to retrieve Request Payload

え?php:// inputって?

PHPのマニュアルによると

php://input は読み込み専用のストリームで、 リクエストの body 部から生のデータを読み込むことができます。
PHP: php:// - Manual

つまりサーバーにリクエストとして送ったデータそのものを取得できるようです。イマイチ$_POSTととの違いがわかりませんよね。

ところでどうしてpayloadで送信されたデータを$_POSTを介して取得出来ないかというと答えはマニュアルに書いてありました。

Content-Type に application/x-www-form-urlencoded あるいは multipart/form-data を用いた HTTP リクエストで、 HTTP POST メソッドから現在のスクリプトに渡された変数の連想配列です。
PHP: $_POST - Manual

「どうしてpayloadの送信を$_POSTで取得できないか?」と言うよりそもそも$_POSTの定義としてpayloadをカバーしていないということです。マニュアル読むの大事ですね。

さらに調べてみると、application/x-www-form-urlencodedmultipart/form-data-encodedの2つはブラウザが必ずサポートしなければならないコンテンツタイプなのでPHPでもこれらにアクセスするためのラッパーとして$_POSTを用意しているようです。

ajax - PHP "php://input" vs $_POST - Stack Overflow

2.クライアントからFormDataとして送信する方法

いつもどおり$_POSTを介して取得するにはコンテンツタイプをapplication/x-www-form-urlencodedにして送信すればOKでこれはBackboneでは以下の用にして設定できます。

Backbone.emulateJSON = true

Backbone.js

ちなみにangularの場合はこちらで紹介されています。
angularJSの$httpで送ったデータを$_POSTで受け取る - Qiita

以上です。
何か誤りを見つけられましたらご指摘いただけると大変ありがたいです!コメント欄でも@egapoolでも。