【PHP】POSTの受け取りで application/x-www-form-urlencoded とapplication/json の両方に対応

変更中につき不確定だよッ!!現在、同アカウントカラ??以内ニ?回リプを受ヲ取ッタラ止マルヨウニなってるんダヨッ!
摂取心光常照護 巳能雖破無明闇 貧愛瞋憎之雲霧 常覆真実信心天 譬如日光覆雲霧 雲霧之下明無闇 獲信見敬大慶喜 即横超截五悪趣 一切善悪凡夫人 問信如来弘誓願 仏言広大勝解者 是人名分陀利華 弥陀仏本願念仏 邪見僑慢悪衆生 信楽受持甚以難 難中之難無過斯
るきでがとこぶ並についあはれお、れば取け受をれそらかたんあ、ずま
\人は1人では生きていけないから支え合うんだよ/ \甘えるな依存するな自立しろ/ 極 端 な 受 け 取 り 方 大 事 な の は 程 よ い 距 離 感
明日は柔道の昇段審査。受が下手くそなので取をそれなりに頑張ってくる。絶対に黒帯とるぞ。
軍艦取銘傳號,說劉銘傳在台有功根本是無稽之談! 劉銘傳在對太平天國作戰時,頭部即受槍傷嚴重,加上眼疾西醫診治幾近眼盲。為減輕政務,曾二次上書朝廷請求離開福建巡撫本缺前往台灣專辦防務。事實上在台灣強徵暴斂剝削打混六年,1890年才被迫辭去台灣巡撫職務。
降雪导致北京南站多趟列车停运晚点 旅客滞留车站: 原标题:降雪导致北京南站多趟列车停运、晚点 旅客滞留 今日上午,北京南站检票口前大量乘客排队候车。 北京南站售票处,已取票乘客排队改签。新京报记者 裴剑飞 摄 新京报快讯(记者 裴剑飞)1月22日上午,受北京周边降雪影响,北京南…
十二(支)縁起 一、無明 二、行 三、識 四、名色 五、六処 六、触 七、受 八、愛 九、取 十、有 十一、生 十二、老死 初めてのインド哲学65
マ シ ン ク エ ス ト の 報 酬 受 け 取 り ま し た か ?
本 渡 そ う と 思 っ た け ど 受 け 取 っ て も ら え な い 気 が し た

はじめに

Vue.jsではAjax通信でaxiosを使うのがスタンダードということで試してみたところ、axios公式のPOSTのExampleの通りに

JavaScript
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

で送信しても、PHPの $_POSTや$_REQUEST では値を受け取れません。これは、PHPがPOSTを Content-type: application/x-www-form-urlencoded のForm Dataの形式で受けるようになっているのに対し、Axiosのデフォルトでは Content-type: application/json でPayloadとして送信しているのが原因です。
参考: ブラウザの時にaxiosのPOSTの値が送信されない?そんなことはない
参考: backboneからのajaxがpayloadでPHPの$_POSTで取得できない

対策としては公式や上記の記事にあるように URLSearchParams を用いて application/x-www-form-urlencoded形式で送信する方法と、file_get_contents(‘php://input’) でapplication/json形式のPayloadを読みに行く方法などがあるようです。
参考: PHPでHTTP POSTされたJSON本文のbodyを受け取る方法

送信側で application/x-www-form-urlencoded に変換してもよいのですが、世の中の流れ的には application/json に傾きつつあるようですし、APIなどではどちらでも受け付けた方が親切だろうということで、受け側のPHPで両方に対応することを考えてみました。
使用したPHPのVersionは 7.1.11 です。

サンプルコード

まず最初に考えたのが以下のコードです。
目標は $request の中身が application/json の場合と application/x-www-form-urlencoded の場合で同じになることです。

PHP
$content_type = explode(';', trim(strtolower($_SERVER['CONTENT_TYPE'])));
$media_type = $content_type[0];

if ($_SERVER['REQUEST_METHOD'] == 'POST' && $media_type == 'application/json') {
    // application/json で送信されてきた場合の処理
    $request = json_decode(file_get_contents('php://input'), true);
} else {
    // application/x-www-form-urlencoded で送信されてきた場合の処理
    $request = $_REQUEST;
}

$_SERVER[‘CONTENT_TYPE’]に Content-type が ‘application/json; charset=utf-8’ のような形で入っているので、セミコロンの前までを切り出して $media_type に入れています。正規表現を用いた方がすっきりするかもしれません。$media_type が ‘application/json’ の場合は

$request = json_decode(file_get_contents('php://input'), true);

でPayloadの内容を読んで、連想配列にデコードします。

大抵の場合はこれで問題ないのですが、

JSON
{
    "id": 1,
    "name": {
        "first_name": "John",
        "last_name": "Doe"
    }
}

このような階層構造になったJSONデータをPOSTした場合、$_REQUEST[‘name’]の中身は連想配列ではなく {“first_name”: “John”,”last_name”: “Doe”} という文字列になるので、これを連想配列にデコードする必要があります。

PHP
$content_type = explode(';', trim(strtolower($_SERVER['CONTENT_TYPE'])));
$media_type = $content_type[0];

if ($_SERVER['REQUEST_METHOD'] == 'POST' && $media_type == 'application/json') {
    // application/json で送信されてきた場合の処理
    $request = json_decode(file_get_contents('php://input'), true);
} else {
    // application/x-www-form-urlencoded で送信されてきた場合の処理
    foreach ($_REQUEST as $key => $value) {
        $request[$key] = json_decode($value, true);

        // json_decodeはクォートされていない文字列がnullになるので戻す
        if ($request[$key] == null) {
            $request[$key] = $value;
        }
    }
}

$_REQUEST をforeachで回してjson_decodeすればOK・・・と思ったらjson_decode関数はクォートされていない文字列がnullになるという謎仕様らしいので、nullになったら元の値に戻すという処理を入れています。あまりスマートではありませんが・・・。

感想など

  • 今まであまりPOST時の Content-type やJSONデータの受け取り方を意識していなかったのでよい勉強になりました。でも将来的にはPHPの標準仕様で対応して欲しいですね。
  • ご意見やもっと良い方法があればコメントをよろしくお願いします。

  • コメント

    1. 匿名希望
      2018/01/21(日) 11:09:32

      るきでがとこぶ並についあはれお、れば取け受をれそらかたんあ、ずま

    2. 匿名希望
      2018/01/21(日) 11:09:32

      摂取心光常照護 巳能雖破無明闇 貧愛瞋憎之雲霧 常覆真実信心天 譬如日光覆雲霧 雲霧之下明無闇 獲信見敬大慶喜 即横超截五悪趣 一切善悪凡夫人 問信如来弘誓願 仏言広大勝解者 是人名分陀利華 弥陀仏本願念仏 邪見僑慢悪衆生 信楽受持甚以難 難中之難無過斯

    3. 匿名希望
      2018/01/21(日) 11:09:32

      変更中につき不確定だよッ!!現在、同アカウントカラ??以内ニ?回リプを受ヲ取ッタラ止マルヨウニなってるんダヨッ!

    4. 匿名希望
      2018/01/21(日) 11:09:32

      軍艦取銘傳號,說劉銘傳在台有功根本是無稽之談!
      劉銘傳在對太平天國作戰時,頭部即受槍傷嚴重,加上眼疾西醫診治幾近眼盲。為減輕政務,曾二次上書朝廷請求離開福建巡撫本缺前往台灣專辦防務。事實上在台灣強徵暴斂剝削打混六年,1890年才被迫辭去台灣巡撫職務。

    5. 匿名希望
      2018/01/21(日) 11:09:32

      明日は柔道の昇段審査。受が下手くそなので取をそれなりに頑張ってくる。絶対に黒帯とるぞ。

    記事に戻る

関連記事