Laravel ルート保護とカスタマイズエラーページ
このページ、Laravelをやっているなら、誰しも一度は見たことがあるでしょう。
404だけでなく、ルート保護のために、開発者が意図的にエラーページへリダイレクトすることがしばしばあります。また、Not Found
ではなく、他のメッセージとかにしたいな、と思ったりすることもあります。今回はルート保護とエラーページのカスタマイズについて見ていきます。
エラーページカスタマイズ
エラーの種類により、それぞれ違うページにリダイレクトしたい場合があります。例えば、404はページ・リソースが見つからない、403は権限が足りない、401は認証・ログインしていない、500はサーバーエラーなど、エラーコードにより分けることが想定できるでしょう。
Laravelではこれらのページのブレードファイルが作成されています。それらのファイルを変更したい場合、まずはリソースフォルダーにパブリッシュする必要があります。
php artisan vendor:publish --tag=laravel-errors
これでresources/views/errors
のフォルダーにデフォルトのブレードファイルがコピーされます。実際にエラーページにリダイレクトする時はこちらのフォルダーのファイルを優先的に使われます。デフォルトとして、401, 403, 404, 419, 429, 500, 503のエラーコードと対応するページがあります。
コントローラなどではabort
関数を使うことで、これらのエラーページにリダイレクトされます。例えば、abort(400,'不正なアクセスです')
など。それで、ブレードビューの中では、abort
関数に渡されたメッセージを受け取ることが可能です。ちなみにabort
関数は任意のResponse
オブジェクトを引数として受け入れられます。
今回は例として、400と405のページを追加します。404.blade.php
からブレードコードをコピーして、タイトル、コードとメッセージを変更します。
@extends('errors::minimal')
@section('title', __('Bad Request'))
@section('code', '400')
@section('message', __($exception->getMessage() ?: 'アクセスが不正です'))
すべてのエラーページはminimal.blade.php
を継承しています。そのため、スタイリングのカスタマイズはこちらとなります。ただ、BootstrapとかのCSSフレームワークは効かないので、ヘッダーのセクションで直接cssを書くのが良いでしょう。
例えば、今回は前のページに戻るボタンをつけようとします。
まずはボタンのブロックを追加します。
<body>
<div class="full-height flex-center vertical">
<div class="position-ref flex-center mb-2">
<div class="code">
@yield('code')
</div>
<div class="message p-1">
@yield('message')
</div>
</div>
<div class="position-ref flex-center">
<a href="{{ url()->previous() }}" class="back-btn">戻る</a>
</div>
</div>
</body>
少しスタイリングを:
.mb-2 {
margin-bottom: 2rem;
}
.p-1 {
padding: 1rem;
}
.back-btn {
color: #fff;
background-color: #1e5ebe;
border-color: #1e5ebe;
font-weight: 400;
text-decoration: none;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
font-size: 0.9rem;
line-height: 1.6;
border-radius: 0.25rem;
}
これですべてのエラーページに元のページに戻るリンクが付けられます。
Ajaxリクエストのルート保護
エラーページができましたので、次にルート保護を見ていきます。前節では触れましたが、ここではabort
関数を使います。
よくあるパターンの1つとして、とあるルートがajaxリクエストのみを扱っていて、直接ブラウザーからURLを打って行こうとしたら、エラーページにリダイレクトしたい。という場合に、コントローラで次のように書けます:
// とあるコントローラで
if ($request->ajax()) {
abort(400);
}
あるいは、もっとオススメなやり方として、カスタマイズのミドルウェアを作って処理します。
php artisan make:middleware AjaxOnly
これでapp\Http\Middleware
フォルダーにファイルが作られますが、次のコードを加えます:
namespace App\Http\Middleware;
use Closure;
class AjaxOnly
{
public function handle($request, Closure $next)
{
if (!$request->ajax()) {
abort(400, 'アクセスが不正です');
}
return $next($request);
}
}
最後に、必要なルートにこのミドルウェアを付ければOKです。
// routes/web.php
Route::get('/ajax_url', 'AjaxController@index')->middleware('ajax.only');
HTTPリクエストのルート保護
もう一つよくあるパターンとして、このルートはPOSTリクエストとか、DELETEリクエストとかのみに使いたい、GETしようとするときはエラーページにリダイレクトしたいと。
一番シンプルなやり方として、HTTPメソッドが不正のエクセプションを利用し、app\Exception\Handler.php
ファイルを編集します。
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
// ...
public function render($request, Exception $exception)
{
if ($exception instanceof MethodNotAllowedHttpException) {
abort(405, '不正なリクエストです');
}
return parent::render($request, $exception);
}
web.php
などのルートファイルに対応するHTTPメソッドが定義されていない場合、MethodNotAllowedHttpException
とのエクセプションが出るため、一概に405のページにリダイレクトします。
以上です!
Discussion