michimani.net

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

2018-01-29

Laravel プロジェクトで作成した API で JSON を返却する際に、ヘッダーに余分なものが付いていたので、その対応方法です。

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

というのがあります。

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

<?php
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を返却する場合

<?php
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 OKCache-Control: no-cache, no-cache?? Content-Type: text/html??? Set-Cookie残ったまま????

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

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

<?php
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"}

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


comments powered by Disqus