PHP
JSON
laravel

Laravelでjsonを返却するときにContent-Typeがapplication/jsonにならない、ヘッダー情報に不要なものが付与される時の対応

今回jsonを返却するときの要件として、

  • ステータスコードを指定したい
  • Cache-Controlno-cacheのみ指定したい
  • Set-Cookie は付与したくない

というのがあります。

Laravelの response ファザードを使用してjsonを返す場合

public function test_json($response_code = 200)
{
    $res = ['foo' => 'bar'];
    $json = json_encode($res);

    return response($json, $response_code)
                ->header('Cache-Control', 'no-cache')
                ->header('Content-Type', 'application/json')
                ->header('Content-Length', strlen($json));
}

test_json(400);

curlでtest_json()を実行するurlを叩いた結果が下記。

$ curl -v http://localhost/path/to/test_json
> GET /path/to/test_json HTTP/1.1
> Host: localhost
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Date: Wed, 27 Dec 2017 04:19:00 GMT
< Server: Apache/2.2.31 (Unix) mod_wsgi/3.5 Python/2.7.13 PHP/7.1.1 mod_ssl/2.2.31 OpenSSL/1.0.2j DAV/2 mod_fastcgi/2.4.6 mod_perl/2.0.9 Perl/v5.24.0
< X-Powered-By: PHP/7.1.1
< Cache-Control: no-cache, private
< Content-Length: 13
< Set-Cookie: XSRF-TOKEN=eyJpd....RhMTUifQ%3D%3D; expires=Wed, 27-Dec-2017 04:54:07 GMT; Max-Age=7200; path=/
< Set-Cookie: 91bb0181f7d...908eb59101d6226=eyJ....ODgyIn0%3D; expires=Thu, 28-Dec-2017 02:54:07 GMT; Max-Age=86400; path=/; HttpOnly
< Connection: close
< Content-Type: application/json
<
* Closing connection 0
{"foo":"bar"}

一見問題なさそうですが、まず Cache-Controlprivate が付いている。ぐぐってみると別に問題なさそうですが、気持ち悪いです。
あと、Set-Cookie で不要なものが付いている。これに関してはミドルウェアでごちゃごちゃやると付与しないように出来るみたいです。今回はやりません。

純粋にPHPの関数だけでjsonを返却する場合

public function test_json($response_code = 200)
{
    $res = ['foo' => 'bar'];
    $json = json_encode($res);

    http_response_code($response_code);
    header('Cache-Control: no-cache');
    header('Content-Type: application/json');
    header('Content-Length: '.strlen($json));
    echo $json;
}

test_json(400);

urlを叩きます。

$ curl -v http://localhost/path/to/test_json
> GET /path/to/test_json HTTP/1.1
> Host: localhost
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 27 Dec 2017 04:30:58 GMT
< Server: Apache/2.2.31 (Unix) mod_wsgi/3.5 Python/2.7.13 PHP/7.1.1 mod_ssl/2.2.31 OpenSSL/1.0.2j DAV/2 mod_fastcgi/2.4.6 mod_perl/2.0.9 Perl/v5.24.0
< X-Powered-By: PHP/7.1.1
< Cache-Control: no-cache, no-cache
< Set-Cookie: XSRF-TOKEN=eyJpd....RhMTUifQ%3D%3D; expires=Wed, 27-Dec-2017 04:54:07 GMT; Max-Age=7200; path=/
< Set-Cookie: 91bb0181f7d...908eb59101d6226=eyJ....ODgyIn0%3D; expires=Thu, 28-Dec-2017 02:54:07 GMT; Max-Age=86400; path=/; HttpOnly
< Content-Length: 13
< Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host localhost left intact
{"foo":"bar"}

HTTP/1.1 200 OK
Cache-Control: no-cache, no-cache??
Content-Type: text/html???
Set-Cookie残ったまま????

色々とおかしいです。
挙動的には、echo したあとにLaravel側の何かしらの処理が動いている感じです。多分。

しばらく悩んだ末の解決策

public function test_json($response_code = 200)
{
    $res = ['foo' => 'bar'];
    $json = json_encode($res);

    http_response_code($response_code);
    header('Cache-Control: no-cache');
    header('Content-Type: application/json');
    header('Content-Length: '.strlen($json));
    echo $json;
    exit();
}

test_json(400);

echo したあとに exit() するだけです。

$ curl -v http://localhost/path/to/test_json
> GET /path/to/test_json HTTP/1.1
> Host: localhost
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Date: Wed, 27 Dec 2017 04:44:56 GMT
< Server: Apache/2.2.31 (Unix) mod_wsgi/3.5 Python/2.7.13 PHP/7.1.1 mod_ssl/2.2.31 OpenSSL/1.0.2j DAV/2 mod_fastcgi/2.4.6 mod_perl/2.0.9 Perl/v5.24.0
< X-Powered-By: PHP/7.1.1
< Cache-Control: no-cache
< Content-Length: 13
< Connection: close
< Content-Type: application/json
<
* Connection 0
{"foo":"bar"}

長時間悩んだときの解決策って、だいたいあっさりしてます。