2015 Laravel Advent Calendar 14日目! Laravel大好き @M_Ishikawa です。
さて、1日目の記事ではエラーログについての内容でしたが、今回もエラーに関する内容です。
エラーページをカスタマイズしようとググればいくらでも答えが見つかります。
それらは
resources/views/errors/404.blade.php
を編集すればいいとか、他のステータスコードに対応させたければ404を該当ステータスコードに変えたものを用意すればいいとか、です。
resources/views/errors/404.blade.php
resources/views/errors/500.blade.php
resources/views/errors/503.blade.php
...
うーん。
1つのテンプレートファイルで済ませたい!!
みんなそう思わないのかな?ぼくは激しく思います!でもググっても日本語サイトではでてきません。1つのステータスコードに強制したりするのは見かけたけど、いやいやそれじゃヘッダ変わっちゃうじゃん〜。
というわけで、コード追っかければ簡単に答えが見つかるのがLaravelの大きな魅力の1つ。レッツ・トライ!
結論を急ぐ方は ここ
※なお、Laravel5のコードで説明しています。
まず、エラーページをレンダリングするのは app/Exceptions/Handler.php の render() です。
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
return parent::render($request, $e);
}
親を追っかけます。
vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php の render()
public function render($request, Exception $e)
{
if ($this->isUnauthorizedException($e)) {
$e = new HttpException(403, $e->getMessage());
}
if ($this->isHttpException($e)) {
return $this->toIlluminateResponse($this->renderHttpException($e), $e);
} else {
return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
}
}
ここでエラーページテンプレートを指定しているのは renderHttpException()
ですね。
vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php の renderHttpException()
protected function renderHttpException(HttpException $e)
{
$status = $e->getStatusCode();
if (view()->exists("errors.{$status}")) {
return response()->view("errors.{$status}", ['exception' => $e], $status);
} else {
return $this->convertExceptionToResponse($e);
}
}
発見しました!
"errors.{$status}"
というふうにベタ書きされています。操作するにはこの関数をオーバーライドするだけでよさそうです。app/Exceptions/Handler.php
の下の方にコピー&貼り付けして編集します。テンプレートファイル名を common.blade.php
にしたいので
/**
* 共通エラーページ
*/
protected function renderHttpException(\Symfony\Component\HttpKernel\Exception\HttpException $e)
{
$status = $e->getStatusCode();
return response()->view("errors.common", ['exception' => $e], $status);
}
を追記すればOKです。
ここで気づくのですが、エラーページには $exception
でHttpExceptionが渡るのですね。ステータスコードやエラーメッセージを渡さないといけないと思っていたのですが、そんなことしなくてもこれをこのまま使うことができます。
というわけで共通化のエラーページはこんな感じにしてみました。本来はphpのコードの部分は上記の app/Exceptions/Handler.php
に入れてしまうべきかもですがここではあえてbladeに直書きしてみます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
</head>
<body>
@php
$status_code = $exception->getStatusCode();
$message = $exception->getMessage();
if (! $message) {
switch ($status_code) {
case 400:
$message = 'Bad Request';
break;
case 401:
$message = '認証に失敗しました';
break;
case 403:
$message = 'アクセス権がありません';
break;
case 404:
$message = '存在しないページです';
break;
case 408:
$message = 'タイムアウトです';
break;
case 414:
$message = 'リクエストURIが長すぎます';
break;
case 419:
$message = '不正なリクエストです';
break;
case 500:
$message = 'Internal Server Error';
break;
case 503:
$message = 'Service Unavailable';
break;
default:
$message = 'エラー';
break;
}
}
@endphp
<h1>{{ $status_code }} {{ $message }}</h1>
</body>
</html>
・・・
ちなみに実はこれ悩んだ経緯がありまして。 abort()
使ってエラー吐き出すと status code が思うように取れなかったんです。
{{ http_response_code() }}
も
@php $guzzle = new GuzzleHttp\Psr7\Response; echo($guzzle->getStatusCode()); @endphp
も200でしか返って来なかったのです。
つまりbladeのレンダリングするタイミングではこれらは200でとり扱ってるからなんですね。
どうやったら取れるかたどったところ、このように $exception
で取れることを見つけたのでした。
いじょーでした!
enjoy! Laravel!!
改定
(2019-05-18) Laravel5.3以降から@php
が使えるようになったので変更しました。5.2以前は@php
は<?php
, @endphp
は?>
に読み替えてください。あとuse
端折ってたのですがコードがコピペで動くように改良しました。