この記事を三行にまとめると
application/x-www-form-urlencodedapplication/json
JSONペイロードの可能性を探ってみるのが良い
Ajaxなどを使ってデータをPOSTすることはよくあると思うんですが、送信されたデータをPHPで受け取る場合、送り方によって$_POSTで値を取れる場合と取れない場合があります。
簡単に言うと、Content-Typeが「application/x-www-form-urlencoded」だと$_POSTで受け取れますが、「application/json」だと受け取れません。
とりあえず両方見てみましょう。
今回はajaxリクエストするURLを「post.php」、PHPで受け取るURLを「receive.php」とします。
だいぶざっくりですが、とりあえずこんな感じでフォームデータを送信できますね。
この場合、$_POSTの中身はこうなります。
特に問題はないですね。普通にフォームのデータを$_POSTで受け取れています。
今回は明示的にContent-Typeを指定しましたが、省略した場合は自動的に「application/x-www-form-urlencoded」になります。
「JSON.stringify($(‘form’).serializeArray())」ってのはフォームの内容をJSONの形に直すやり方です。詳細については今回は省略。
こんな感じでデータをPOSTすると$_POSTの中身は空になります。
じゃあどうやってJSONデータを取れば良いのかってーと、こうです。
「php://input」は読み込み専用のストリームです。リクエストの body 部から生のデータを読み込むことができるとマニュアルには書いてあります。まあ何のことだかよく分かんねっすけど、application/jsonでJSONデータをPOSTした場合はリクエストの中身がJSONデータになるんで、それをそっくりそのまま取得したけりゃ「file_get_contents(“php://input”)」と書けってことですね、きっと。
ちなみに今回の場合だとreceive.phpの$jsonの中身はこんな風になってます。
$_POSTの時と少し違いますね。これはJSON形式でデータを送ると必ずこうなるってわけじゃなくて、「JSON.stringify($(‘form’).serializeArray())」でデータをJSON形式に直した場合にこうなるってだけです。自分でフォームのデータを送信する前に整形して「[{name:’name’},{age:30}]」みたいなJSONデータに直してあげれば$_POSTの時と同じような形になります。
いろんなAPIを使っていると、$_POSTでデータを受け取れる時もあれば取れない時もあります。最近自分が触った中ではFacebookのMessengerとかメール配信サービスのSendgridのAPIはJSONペイロードでデータをPOSTしてくるので、$_POSTではデータを取れませんでした。
何はともあれ、$_POSTでデータが取れなくても焦らず落ち着いて、まずはJSONペイロードの可能性を探ってみるのが良いかと思います。
簡単に言うと、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ペイロードの可能性を探ってみるのが良いかと思います。