108
129

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel5: エラーページを共通化〜どんなステータスコードでもどんと来い!

Last updated at Posted at 2015-12-14

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() です。

app/Exceptions/Handler.php
    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()

vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php
    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()

vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php
    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 にしたいので

app/Exceptions/Handler.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に直書きしてみます。

resources/views/errors/common.blade.php
<!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端折ってたのですがコードがコピペで動くように改良しました。

108
129
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
108
129

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?