[Laravel] JSONレスポンスのUnicodeエスケープ無効化はミドルウェアで!
こんにちは、ナカエです。Unityの記事を書こうと思っていたのですがネタを見つけてしまったので今回もLaravelについての記事です。
PHPユーザーズ(日本語)の#laravelチャンネルのログで
PSR-15厨なのでミドルウェアで解決します。HTTPレスポンスに関わる共通処理の置き所といえばミドルウェアです。
今回要求されるロジックは"アンエスケープが設定されていないJsonResponseをアンエスケープされたものに差し替える"。とても単純ですね。
PHPユーザーズ(日本語)の#laravelチャンネルのログで
- JSONレスポンスをブラウザで確認するときにUnicodeエスケープされているので解除したい
- いちいち個別にJSON_UNESCAPED_UNICODEオプションを指定してまわるのは避けたい
- LaravelのJsonResponse生成が直でnewされていたりするのでフレームワークの該当箇所を全て差し替えるのも辛い
Laravelのjson応答でユニコードのエスケープをやめさせる
マルチバイト圏特有の問題なので英語圏では気にならないんでしょうね。別の解決方法のアイデアとその簡易実装について書きたいと思います。HTTP周りの共通処理はミドルウェア
今回要求されるロジックは"アンエスケープが設定されていないJsonResponseをアンエスケープされたものに差し替える"。とても単純ですね。
ミドルウェアの実装は例えば下記のようになります。
<?php
namespace App\Http\Middleware;
use Symfony\Component\HttpFoundation\JsonResponse;
class UnescapeJsonResponse
{
/**
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handle($request, \Closure $next)
{
$response = $next($request);
// JSON以外はそのまま
if (!$response instanceof JsonResponse) {
return $response;
}
// エンコードオプションを追加して設定し直す
$newEncodingOptions = $response->getEncodingOptions() | JSON_UNESCAPED_UNICODE;
$response->setEncodingOptions($newEncodingOptions);
return $response;
}
}
解説
実装にあたって、IlluminateのJsonResponseの継承元であるSymfony\Component\HttpFoundation\JsonResponseを調べました。流石は天下のSymfonyのhttp-foundation、今回の処理を9割方を解決してくれるメソッドがあります。こちらのgetterとsetterの2つがあれば、あとはエンコードオプションにビット和でJSON_UNESCAPED_UNICODEを追加するのみです。
<php
namespace Symfony\Component\HttpFoundation;
class JsonResponse extends Response
{
/**
* Returns options used while encoding data to JSON.
*
* @return int
*/
public function getEncodingOptions()
{
return $this->encodingOptions;
}
/**
* Sets options used while encoding data to JSON.
*
* @param int $encodingOptions
*
* @return $this
*/
public function setEncodingOptions($encodingOptions)
{
$this->encodingOptions = (int) $encodingOptions;
return $this->setData(json_decode($this->data));
}
}
使い方
Kernelに上記ミドルウェアを追加するのみです。JSONレスポンス以外はそのまま素通しします。
実用上は、デバッグモード時のみこのミドルウェアを有効にするなど少し改良が必要でしょう。