経緯
JavaScript の AJAX通信 用パッケージ ky
から content-type: application/json
を指定(デフォルト) して HTTP の POSTメソッド でデータを投げたところ、 PHP の filter_input
で受け取れなかったため嵌まりました。
コード
例えばフロントエンド (React, TypeScript) で以下のように ky
を使用して AJAX通信 させようとします。
import ky from 'ky-universal';
type Props = {
apibaseurl: string,
};
type Data = {
id?: number,
name?: string,
};
type ResWrite = {
statusCode: number,
data: Data[],
};
const SomeComponent = ({ apibaseurl, }: Props) => {
const res: ResWrite = await ky.post(
`${apibaseurl}write/user/id/`,
{
json: { user_id: userId, }
}
).json();
if(res.data.length > 0 && typeof res.data[0].name === 'string') {
// some processing ...
}
return (
<>
...
</>
);
};
export default SomeComponent;
対応するバックエンド(PHP, ルータは FastRoute 使用で自前コード)は以下のような感じ。
class RepositoryUser
{
// 略
/**
* @return array
*/
public function write(): array
{
$userId = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);
if($userId === $some_value) {
// some proccessing...
}
return $someArray;
}
}
ご覧の通り、 filter_input
を使用して POSTメソッド で投げられてくるであろうキー user_id
の値を受け取ろうとしたのですが、一向に入ってくる気配がなく嵌まってしまいました……。
原因・結果
PHP の公式リファレンスに答えが記されていました。
Content-Type に application/x-www-form-urlencoded あるいは multipart/form-data を用いた HTTP リクエストで、 HTTP POST メソッドから現在のスクリプトに渡された変数の連想配列です。
……つまり、 content-type: application/json
が指定されたデータは $_POST
や filter_input
では受け取れないということが分かりました。正しい仕様の挙動だったのですね……。
これを踏まえて、 file_get_contents
を使用して値を受け取るように PHP コードを以下のように改修しました。
改修後のコード
class RepositoryUser
{
// 略
/**
* @return array
*/
public function write(): array
{
// ここを改修
// NG: $userId = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);
$jsonStr = file_get_contents('php://input');
$jsonArray = $this->helper->jsonDecode($jsonStr);
if(array_key_exists('user_id', $jsonArray)) {
$userId = (int)$jsonArray['user_id'];
}
if($userId === $some_value) {
// some proccessing...
}
return $someArray;
}
}
これで無事値を受け取ることができました。
分かるまで結構長い時間を取られてしまいましたが、まさか仕様通りだったとは……。