jsonペイロードと$_POSTについて

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
スペースシャトルの貨物室のことをペイロード・ベイと言うそうです

この記事を三行にまとめると

application/x-www-form-urlencoded
application/json
JSONペイロードの可能性を探ってみるのが良い
Ajaxなどを使ってデータをPOSTすることはよくあると思うんですが、送信されたデータをPHPで受け取る場合、送り方によって$_POSTで値を取れる場合と取れない場合があります。

簡単に言うと、Content-Typeが「application/x-www-form-urlencoded」だと$_POSTで受け取れますが、「application/json」だと受け取れません。

とりあえず両方見てみましょう。

今回はajaxリクエストするURLを「post.php」、PHPで受け取るURLを「receive.php」とします。



application/x-www-form-urlencoded

例えばjQueryの$.ajaxを使ってフォームデータを送信する場合を考えてみます。

//post.php
<form>
  <input type="hidden" name="name" value="name" />
  <input type="hidden" name="age" value="30" />
  <button type="button" id="post">button</button>
</form>

<script>
$(function(){
  $('#post').click(function(){
    $.ajax({
      type: 'POST',
      url: 'receive.php',
      data: $('form').serialize(),
      contentType: 'application/x-www-form-urlencoded'
    }).then(function(data){
      console.log(data);
    });
  });
});
</script>

//receive.php
print_r($_POST);

だいぶざっくりですが、とりあえずこんな感じでフォームデータを送信できますね。

この場合、$_POSTの中身はこうなります。

Array
(
  [name] => name
  [age] => 30
)

特に問題はないですね。普通にフォームのデータを$_POSTで受け取れています。

今回は明示的にContent-Typeを指定しましたが、省略した場合は自動的に「application/x-www-form-urlencoded」になります。



application/json

Content-Typeが「application/json」の場合、フォームのデータはJSON形式で送信することになります。JSONペイロードとか言うこともあるみたいですが、これだと$_POSTに値が入ってきません。

//post.php
<form>
  <input type="hidden" name="name" value="name" />
  <input type="hidden" name="age" value="30" />
  <button type="button" id="post">button</button>
</form>

<script>
$(function(){
  $('#post').click(function(){
    $.ajax({
      type: 'POST',
      url: 'receive.php',
      data: JSON.stringify($('form').serializeArray()),
      contentType: 'application/json'
    }).then(function(data){
      console.log(data);
    });
  });
});
</script>

「JSON.stringify($(‘form’).serializeArray())」ってのはフォームの内容をJSONの形に直すやり方です。詳細については今回は省略。

こんな感じでデータをPOSTすると$_POSTの中身は空になります。



じゃあどうやってJSONデータを取れば良いのかってーと、こうです。

//receive.php
$data = file_get_contents("php://input");
$json = json_decode($data, true);
print_r($json);

「php://input」は読み込み専用のストリームです。リクエストの body 部から生のデータを読み込むことができるとマニュアルには書いてあります。まあ何のことだかよく分かんねっすけど、application/jsonでJSONデータをPOSTした場合はリクエストの中身がJSONデータになるんで、それをそっくりそのまま取得したけりゃ「file_get_contents(“php://input”)」と書けってことですね、きっと。

ちなみに今回の場合だとreceive.phpの$jsonの中身はこんな風になってます。

Array
(
  [0] => Array
  (
    [name] => name
    [value] => name
  )

  [1] => Array
  (
    [name] => age
    [value] => 30
  )
)

$_POSTの時と少し違いますね。これはJSON形式でデータを送ると必ずこうなるってわけじゃなくて、「JSON.stringify($(‘form’).serializeArray())」でデータをJSON形式に直した場合にこうなるってだけです。自分でフォームのデータを送信する前に整形して「[{name:’name’},{age:30}]」みたいなJSONデータに直してあげれば$_POSTの時と同じような形になります。






いろんなAPIを使っていると、$_POSTでデータを受け取れる時もあれば取れない時もあります。最近自分が触った中ではFacebookのMessengerとかメール配信サービスのSendgridのAPIはJSONペイロードでデータをPOSTしてくるので、$_POSTではデータを取れませんでした。

何はともあれ、$_POSTでデータが取れなくても焦らず落ち着いて、まずはJSONペイロードの可能性を探ってみるのが良いかと思います。
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください