けけずんセルフハッキング

忘れそうなことをメモる。

Laravel5.5で任意のエラー画面を出すときに注意した方がいいこと

はじめに

Laravel5.5で任意のエラー画面を出したくて奮闘した話。最初は下記の要件を満たしたいだけだった。

  • abort関数でエラーコードに応じた任意の画面を出力する
  • ぬるぽや未定義変数を参照するなど想定外のエラーの際に任意の画面を出力する

これをネットの記事を参考にし、Laravelで例外処理をハンドリングしてるであろうapp/Exceptions/Handler.phprenderメソッドを以下のように編集した。

public function render($request, Exception $exception)
{
+    if ($this->isHttpException($exception)) {
+        if ($exception->getStatusCode() == 400) {
+            return response()->view('errors.400', [], 400);
+        }
+    } else {
+        return response()->view('errors.500', [], 500);
+    }
+
-    return parent::render($request, $exception);
}

とりあえずこれでabort関数で任意のページを表示してくれるし、想定外のエラーが起きた際にも500システムエラーページを表示してくれるようになった。やったぜ!と喜んでいるのもつかの間、問題が発生する。

問題発生!

入力されたデータに対する自動バリデーションが思うように動作しなくなった。なんでだろうと思い編集前のapp/Exceptions/Handler.phprenderメソッドを見直すと、

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

こいつが目に入った。中を覗いてみると以下のようになっていた。

public function render($request, Exception $e)
{
    if (method_exists($e, 'render') && $response = $e->render($request)) {
        return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
        return $e->toResponse($request);
    }

    $e = $this->prepareException($e);

    if ($e instanceof HttpResponseException) {
        return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
        return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
        return $this->convertValidationExceptionToResponse($e, $request);
    }

    return $request->expectsJson()
                    ? $this->prepareJsonResponse($request, $e)
                     : $this->prepareResponse($request, $e);
}

ValidationExceptionの文字が見える。なるほど、parent::renderメソッドでバリデーションとっていたのか。今回はparent::renderメソッドを消していた、道理でバリデートされないわけだ。

無事解決!

app/Exceptions/Handler.phprenderメソッドを再び修正する。

public function render($request, Exception $exception)
{
+    if ($this->isHttpException($exception)) {
+        if ($exception->getStatusCode() == 400) {
+            return response()->view('errors.400', [], 400);
+        }
+    }
+
+    if ($exception instanceof ErrorException) {
+        return response()->view('errors.500', [], 500);
+    }
+
    return parent::render($request, $exception);
}

上記のコードの通り$exceptionHttpExceptionのときは任意のエラーページを表示し、想定していないエラーについてはErrorExceptoinで補足してシステムエラーページを表示する。さらにHttpExceptionErrorException以外の何かしらの例外やエラーについてはparent::renderメソッドで処理をするようにし、自動バリデーションなど適切に処理が行われるようにした。

おわりに

既存のコードに変更を加える前にしっかりと周辺のコードを読み込まないとアカンですね。気をつけよう。

参考