【2時間悩んだ】JavaScriptのfetchでPHPにPOSTしようとしたらうまくいかないぞ

2,829views

JavaScriptのfetch()を知ってからXMLHttpRequestは使わなくなりましたゆひゃです。

今回fetch関数を使ってローカルのPHPにPOST送信したら結構詰まった話です。

思えば「fetch」は英語で「取ってくる」という意味なのにPOSTを送信するってなんか変ですね。

fetchの仕様

fetchはMDNによると、第一引数にAPIのURL、第二引数に送信情報を指定できるようです。

fetch( url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringfy( data )
})
  .then( res => res.json() )
  .then( data => {
    ....
  })

こんな感じで送信できるようです。

試したプログラム

今回はJavaScriptからPHPにPOST送信するだけ(だと思っていた)なので、XMLでやっていた通り以下のプログラムを最初に書きました。

普通に$_POSTで受け取る(だめだった)

JavaScript:localhost:3000/

const url = 'http://localhost:8888/file_archives/'
const postData = {
  operate: 'fetch',
  option: 'none'
}
fetch( url,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(postData)
  } )
  .then( res => res.json() )
  .then( files => {
    setData( files )
  } )

PHP:localhost:8888/file_archives/index.php

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
header("Content-type: application/json; charset=UTF-8");

//var_dump($_POST);
$operate = $_POST['operate'];
....
echo json_encode(...);

POST送信したので$_POSTで受け取ろうとしました。phpのヘッダ設定はクロスオリジンの対応です。色々してJSONを出力しています。

ですが、JavaScript側(localhost:3000)では

Uncaught (in promise) SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data

のエラーが出て、PHP側ではPOSTを受け取れてない感じのエラーが出て、var_dump($_POST)をしてみるとPOSTは空でした。

ターミナルでcURLしてみる(できた)

まさかPHP側に問題がある?かとも思ってとりあえずXMLで送信しようとしました。

% curl -X POST -d 'operate=fetch' http://localhost:8888/file_archives/
>[{...},{...}]

PHPは同じ。

うまくJSONが返されました。

どうやらPHPは$_POST受取するのは問題なさそうです。

XMLで送信してみる(まあできた)

一旦慣れた方法のXMLHttpRequestで送信してみます。

JavaScript

const xhr = new XMLHttpRequest()
 
xhr.open('POST', url)
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
xhr.send('operate=fetch')

PHPは同じ。

うまくいきました。$_POSTにちゃんとデータが格納されていて、想定しているJSONが返されました。

ここでリクエストヘッダのXMLの場合は'content-type'を'x-www-form-urlencoded'としていることに気づきました。

うまくいかなかった原因

JavaScript側ではfetch関数で'Content-Type''application/json'となっているのに対し、PHP側では$_POSTで受け取ろうとしていたのが問題でした。

どうやら$_POSTで受け取れるのは'application/x-www-form-urlencoded''multpart/form-data'のどちらかでしかいけないようです。

解決方法1

JavaScript:localhost:3000/

const url = 'http://localhost:8888/file_archives/'
const postData = {
  operate: 'fetch',
  option: 'none'
}
fetch( url,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(postData)
  } )
  .then( res => res.json() )
  .then( files => {
    setData( files )
  } )

PHP:localhost:8888/file_archives/index.php

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
header("Content-type: application/json; charset=UTF-8");

$input_json = file_get_contents('php://input');
$post = json_decode( $input_json, true );
$operate = $post['operate'];
....
echo json_encode(...);

とする必要があるようです。JavaScriptは最初と同じです。

PHP側でPOSTしたデータを受け取るためには、file_get_contents()を使う必要があるようです。なるほど。

解決策2

JavaScript:localhost:3000/

const url = 'http://localhost:8888/file_archives/'
const postData = {
  operate: 'fetch',
  option: 'none'
}
fetch( url,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams(postData).toString()
  } )
  .then( res => res.json() )
  .then( files => {
    setData( files )
  } )

PHP:localhost:8888/file_archives/index.php

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
header("Content-type: application/json; charset=UTF-8");

$operate = $_POST['operate'];
....
echo json_encode(...);

こちらでもうまくいきました。PHP側の処理は最初と同じです。

JavaScriptの'Content-Type''application/x-www-form-urlencoded'を指定して、bodyパラメータにはURLパラメータ文字列を与えて送信しました。

$_POSTはapplication/jsonが受け取れないってだけなので受け取れる形でJavaScriptで送信するだけです。

割と簡単なミスでした。

まとめ

IEから解放されてfetch関数をゴリゴリ使えるようになってAjaxが楽になったと思った矢先、こんな罠に引っかかるとは、、

今後、WebAPIを作る機会も多くなるでしょうから、この経験が活きることを願いますね。

とりあえず解決してよかった。

カテゴリー