経緯
Guzzle で API を叩いていたのですが、レスポンスのステータスコードが 200 なのにボディ部が []
(空配列) だったので嵌まってしまいました。そこで検証のため、ブラウザから POST しようとした、というお話です。
検証
検証のため、以下のようなリクエストヘッダとレスポンスヘッダを出力するだけの PHP を用意します。
<?php
date_default_timezone_set('Asia/Tokyo');
mb_language('ja');
mb_internal_encoding('UTF-8');
$status = [
'code' => 200,
'message' => 'OK.',
];
// request
$requestHeaders = apache_request_headers();
unset($requestHeaders['Cookie']);
$requestHeaders['Method'] = $_SERVER['REQUEST_METHOD'];
$requestBodies = file_get_contents('php://input');
$request = [
'headers' => $requestHeaders,
'bodies' => $requestBodies,
];
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
$status['code'] = 200;
$status['message'] = 'OK.';
break;
case 'POST':
$status['code'] = 200;
$status['message'] = 'OK.';
break;
case 'PUT':
$status['code'] = 201;
$status['message'] = 'Created.';
break;
case 'DELETE':
$status['code'] = 205;
$status['message'] = 'Reset Content.';
break;
case 'HEAD':
$status['code'] = 400;
$status['message'] = 'Bad Request.';
break;
case 'OPTIONS':
$status['code'] = 405;
$status['message'] = 'Method Not Allowed.';
break;
case 'PATCH':
$status['code'] = 409;
$status['message'] = 'Conflict.';
break;
default:
$status['code'] = 405;
$status['message'] = 'Method Not Allowed.';
break;
}
// response
$responseHeaders = [
'Code' => $status['code'],
'Status-Message' => $status['message'],
'Protocol' => $_SERVER['SERVER_PROTOCOL'],
'Host' => $_SERVER['HTTP_HOST'],
'Date' => date('D, d M Y H:i:s T'),
'Connection' => isset($_SERVER['HTTP_CONNECTION']) && !empty($_SERVER['HTTP_CONNECTION']) ? $_SERVER['HTTP_CONNECTION'] : 'Close',
'X-Powered-By' => explode(' ', $_SERVER['SERVER_SOFTWARE'])[0] . '/' . explode(' ', $_SERVER['SERVER_SOFTWARE'])[1],
'Content-Type' => isset($_SERVER['CONTENT_TYPE']) && !empty($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
];
$responseBodies = file_get_contents('php://input');
$response = [
'headers' => $responseHeaders,
'bodies' => $responseBodies,
];
// concat
$output = json_encode(
[
'request' => $request,
'response' => $response
],
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE
);
// output
header('Content-Type: application/json; charset=UTF-8');
header($_SERVER['SERVER_PROTOCOL'] . ' ' . (string)$status['code'] . ' ' . $status['message']);
echo $output;
次に、 Chrome拡張機能 の Talend API Tester をインストールします。
そして、先程の PHP が置いてある Webサーバ に向けて各メソッドとデータの投げ込みを試します。
確かに POST や他のメソッドがリクエストできていることが確認できました。
ここで、 Guzzle で上手く行っていない API に向けて POST したところ、正常なレスポンス、ボディ部もきちんと JSON が返却されていることが確認できました。
この Chrome拡張機能、自作の API の動作確認するとき等で役に立ちそうですね。
原因
さて、本題の Guzzle がコケていた原因ですが……結局はレスポンスのデータを格納している Guzzle のオブジェクトから取り出す方法を間違えていただけでした (処理自体は正しく完了していた)。
具体的には、 Guzzle のレスポンス $rensponse
に対し、 $response->getBody()
ではダメで、 $response->getBody()->getContents()
までしないとレスポンスボディ部の JSONデータ が拾えなかった、というオチ。
なお、今回のケースでは $response->getBody()->getContents()
の出力が文字列なのでそのまま echo
しても良い状態でした。
あるいは、冗長ですが json_encode(json_decode($response->getBody(), true), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE)
と一度 JSON をデコードしてオブジェクトの形で取り出してから、再度 JSON エンコードで JSON文字列 にすれば上述と同様の結果が出力されました。
参考
Chrome拡張機能 (Talend API Tester)
Guzzle
- Request Options — Guzzle Documentation
- PHP の Guzzle を使う時にもうググらない! GuzzleHttp の RequestOptions 定数を使う – 猫でもわかるWebプログラミングと副業
headers
は\GuzzleHttp\RequestOptions::HEADERS
、form_params
は\GuzzleHttp\RequestOptions::FORM_PARAMS
で指定できる