Laravelの404ページやその他の例外ページの作成方法

2019/07/15 2019/12/27 #Laravel #PHP #エラーページ #404

Laravelの404ページやその他の例外ページの作成方法

こんにちは。Nonです。

今回は備忘録としてLaravelでの404ページなどのカスタマイズ方法について書いていきたいと思います。

ページ用のbladeを作成する

普通にbladeとして作成します。

下記の例ではh1タグのみにしていますが、CSSなどを利用してページを装飾することをおすすめします。

<h1>404 Not Found. このページは存在しません。</h1>
<a href="/" class="btn btn-primary">TOPへ戻る</a>

404というか例外時の処理

Laravelはroutesに無い時NotFoundHttpExceptionをスローします。

このスローの結果、最終的に/app/Exceptions/Handler.phprenderメソッドへ処理が移ります。

また、自分のアプリケーションで独自の例外を作成している場合もアプリケーション層でキャッチしていない場合、ここへ処理が来ます。

/**
 * Render an exception into an HTTP response.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Exception $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    return parent::render($request, $exception);
}

ここのrenderメソッドを編集します。

ページがないときの例外

/**
 * Render an exception into an HTTP response.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Exception $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    if ($exception instanceof NotFoundHttpException) {
        return view('pages.errors.404');
    }

    return parent::render($request, $exception);
}

サーバーエラーが発生したときの例外

他の例外時、例えばサーバーエラーを示す500エラーなどの場合は、独自の例外や、Laravelの例外すべてを拾いたいので、このようにするのがいいかもしれません。

/**
 * Render an exception into an HTTP response.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Exception $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    if ($exception instanceof NotFoundHttpException) {
        return view('pages.errors.404');
    }
    
    // 一番最後に書くことを推奨
    if ($exception instanceof Throwable) {
        return view('pages.errors.500');
    }

    return parent::render($request, $exception);
}

ログインに失敗した時や、権限のエラーのときの例外

ログイン時は401 Unauthorizedで、権限エラーは403 Forbiddenです。

しかし、このエラーをここで拾うのはちょっとおかしいかもしれません。

このメソッドはアプリケーションが想定していない入力をされた時に発生する例外をまとめるべきです。

ログインエラーや、アプリケーション権限の例外は想定できる例外ですので、ControllerMiddlewareレベルでtry-catchを使用するべきです。

例えばログインが必要な機能ページの例外はLoggedInMiddlewareなどを作成して自身のログインチェック時にスローされる例外をキャッチしてエラーページをレンダリングするほうが、/app/Exceptions/Handler.phpのコードを汚さなくてすみます。

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @param  string|null  $guard
 * @return mixed
 */
public function handle($request, Closure $next, $guard = null)
{
    try {
         // ログインチェック
    } catch (UnauthorizedHttpException $e) {
        // 認証のエラーページやリダイレクト処理
    }

    return $next($request);
}

ちなみにLaravelの標準で付属している認証システムを利用している場合は、すでにRedirectIfAuthenticatedが実装されているので、不要です。

アプリケーションの権限などの場合はログインと同様にRoleMiddlewareなどを作成し、権限が関係するルーティングにこのミドルウェアを適用し、try-catchを使います。

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @param  string|null  $guard
 * @return mixed
 */
public function handle($request, Closure $next, $guard = null)
{
    try {
         // 権限チェック
    } catch (RoleException $e) {
        // 権限のエラーページやリダイレクト処理
    }

    return $next($request);
}

APIのときとかは?

LaravelのRequestクラスにはexpectsJsonメソッドが実装されているので、これを利用してAPIでコールされているかどうかを確認しましょう。(APIがXMLとかの場合は他の関数を用意して利用しましょう)

/**
 * Render an exception into an HTTP response.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Exception $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    if ($exception instanceof NotFoundHttpException) {
        if ($request->expectsJson()) {
            response()->json([
                'message' => 'Not Found.',
            ]);
        }
        return view('pages.errors.404');
    }
    
    // 一番最後に書くことを推奨
    if ($exception instanceof Throwable) {
        if ($request->expectsJson()) {
            response()->json([
                'message' => 'Server Error.',
            ]);
        }
        return view('pages.errors.500');
    }

    return parent::render($request, $exception);
}

基本的に/app/Exceptions/Handler.phpはあまり利用しないようにしたほうがいいかも

ここに例外がスローされる時はアプリケーション上本当に拾えない例外か、よっぽどの想定外(PHP構文エラーや実装時のバグとか)を拾うためのメソッドです。

なので、アプリケーション上で開発時に想定できる例外はキチンと他の層でキャッチしたほうが可読性が上がります

最後に

今回は備忘録とエラーハンドリングの整理がてらまとめてみました。

最初は404エラーについてにまとめようと思っていましたが、結局それはエラーハンドリングをどのような構造で作成するかということでしたので、他の例外についてもまとめてみました。

正常時の設計も大事で、こちらばかりに注目が行きがちですが、エラーハンドリングをしっかりすることもとても重要です。例外を自分の想定どおりにハンドリングできていないと、運用のときどこでエラーが起きたとかを拾いづらくなってとても困ります

一度適当な記事を書いてしまったので、リライトしました。

これからもLaravelを利用したアプリの設計について思うところがあれば記事を更新したいと思います。

その時はよしなに。

.

のん

所属 : 株式会社スマレジ 開発部

YouTube : のんラボ

Twitter : @nonz250

Github : nonz250

Qiita : @nonz250

My Qiita posts My Qiita contributions My Qiita followers

主にPHPを使用し、サーバーサイドを担当。最近はフロントにも興味津々。

なにかを作ったりいじったりするのが好きで、個人開発なども行っている。

趣味はバイクアイコン画像は大抵愛車の「Z250」である。友達にアイコン描いてもらえて嬉しい。

PHP / Laravel / CakePHP2 / CakePHP3 / Vue / Nuxt / C# / etc...

Tags

#のんラボ #Laravel #Vue #個人開発 #ブログ #プログラミング #javascript #Html5 #WEBサービス #Twitter #今年の抱負メーカー #勉強方法 #PWA #モバイルアプリ #Android #ツーリング #バイクに乗るエンジニア #Z250 #秋吉台 #能登半島 #バイク #冒険 #東尋坊 #Squid #リバースプロキシ #hosts #axios #cropper #AdSense #Bootstrap #MySQL #高速化 #トドTask #Telescope #デバッグ #composer #テスト #セキュリティ #POSレジ #スマレジ #本部機能 #バリデーション #入力チェック #Mac #Chrome #テスト駆動開発 #開発手法 #UI #デザイン #WEBサイト #機能美 #PHP #Laravel 6 #コメント #バージョンアップ #vue-cli #localhost #BIツール #売上分析 #TANAX #MFK250 #ツアーシェルケース2 #RESTful #API #REST API #実務的 #PHP Tech Tutor #Smaregi Tech Talk #勉強会 # ブログ #CakePHP3 #CSRF #VSCode #開発環境 #CakePHP3.0 #さくらのレンタルサーバー #モジュールモード #シェル #メール #Gmail #relay #OGP #エラーページ #抱負 #家庭教師 #ドメイン駆動設計 #DDD #読書会 #那智の滝 #伊勢志摩 #伊勢志摩スカイライン #フロント #三方五湖 #レインボーライン #ボーイスカウト・ルール #プログラマが知るべき97のこと #リファクタリング #ユビキタス言語 #車輪の再発明 #マイクロサービス #デプロイ #QA #laravel-mix #Tips #storybook #@storybook/addon-actions #昇降デスク #コードレス #書斎 #オフィス #リモートワーク #働き方 #エラーハンドリング #スマレジ4 #pixel 5 #レビュー #スマレコ #TDD #RSS #404 #高山ダム #ラーツー #React #Nuxt #node_modules #エラー #インポート #設定方法 #環境構築 #Docker #フォレストパーク神野山 #学生向け #PR #採用 #Node.js #npm