【Laravel】 ミドルウェアの使い方と作り方

ミドルウェアは、リクエストを検査したりユーザ認証をチェックして弾く、アクセスに関する詳細なログを記録するといった、メインの処理を行う前のタスクを実行するのに使用します。

最初からあるミドルウェア

Laravelでは、いくつかのミドルウェアが最初から定義されています。ミドルウェアは、app/Http/Middleware/ディレクトリ内にあります。いずれのミドルウェアもアプリケーションのメインロジック (要はControllerのメソッド) に入る前に処理されるものです。

フォームから送信した際のCSRFの防止(VerifyCsrfToken)、ログイン認証(Authenticate)、入力された情報のトリム (文字列の前後のスペース削除) (TrimStrings) といったものが並んでいます。

最初からあるミドルウェア

ミドルウェアを使う

用意されているミドルウェアや自作したミドルウェアは、app/Http/Kernel.phpに登録することで使用、またはルーティング側に設定することで使用できます。

Kernelで設定

Kernelでは、どのルートでも使用するミドルウェアを$middlewareに、ルーティングのグループごとに使い分けるミドルウェアを$middlewareGroupsに入れます。いくつかは最初から定義されていますね。

前者の、どのルートでも使用するミドルウェアは、グローバルミドルウェアと呼びます。

ここにあるルートのグループというのは、app/Providers/RouteServiceProvider.phpboot()内でどれがどのグループに属するかが定義されています。

// どのページでも使用したいミドルウェアのクラスを並べる
protected $middleware = [
    \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
    // ...
];

// ルートのグループごとに使用したいミドルウェアを並べる
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\VerifyCsrfToken::class,
        // ...
    ],

    'api' => [
        // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

ルートごとに指定

特定のルートにミドルウェアを適用したい場合、Kernel.php$routeMiddlewareに並べ、そのキーを使ってroutes側の定義にメソッドチェーンで->middleware('キー')のようにします。

Kernel.php
protected $routeMiddleware = [
    'hoge' => \App\Http\Middleware\Authenticate::class,
];
web.php
// $routeMiddlewareのキーと、ここのmiddlewareをあわせる
Route::get('/hoge', [HelloController::class, 'index'])
    ->middleware('hoge');

// 配列でもOK
// Route::get('/hoge', [HelloController::class, 'index'])
//     ->middleware(['hoge']);

ミドルウェアの適用から除外

特定のルートで特定のミドルウェアを適用したくない場合、ルート側でwithoutMiddlewareを設定します。引数にはクラスを入れます。

Route::post('/home', [HelloController::class, 'hoge'])
    ->withoutMiddleware([\App\Http\Middleware\TrimStrings::class]);

グローバルミドルウェアとして登録されているミドルウェアは、withoutMiddlewareを設定しても適用外になりません。

ミドルウェアを作る

ミドルウェアを作る場合、まずはartisanでファイルを作成しましょう。

php artisan make:middleware IpRestriction

そうすると、app/Http/Middleware/ディレクトリ内に指定した名前のファイルが作成されています。

public function handle(Request $request, Closure $next)
{
    return $next($request);
}

にあるRequestは、コントローラに設定したRequestと同じです。$request->input('hoge')のようにして、リクエストボディやクエリストリングの値を取得できます。

ルートパラメータを取得したい場合、$request->route()->parameter('userId')のようにします。

use Illuminate\Support\Facades\Log;

public function handle(Request $request, Closure $next)
{
    Log::debug($request->route()->parameter('id')); // 172.22.0.1
    return $next($request);
}

$nextは、今後の処理を行うためのものです。その今後の処理の中には、コントローラのメソッドを実行するなども含まれています。そのため、

public function handle(Request $request, Closure $next)
{
    $response = $next($request);

    // 処理

    return $response;
}

のように、先に$nextを実行すると、コントローラなどの処理後に実行するミドルウェアを作成できます。

実際に作ってみる

実際にミドルウェアを1つ作ってみましょう。今回はIP制限を行うミドルウェアを作成してみます。

php artisan make:middleware IpRestriction

IPアドレスは$request->ip()で取得できます。これを使って特定のIPのみページを表示し、それ以外のIPはエラーページに飛ばす処理を作ってみます。

public function handle(Request $request, Closure $next)
{
    Log::debug($request->ip()); // 172.22.0.1など
    
    return $next($request);
}

リダイレクトするには、redirect('path', statusCode)関数を用います。

public function handle(Request $request, Closure $next)
{
    if ($request->ip() === '172.22.0.1') {
        // 続きの処理を実行
        return $next($request);
    }
    
    // 172.22.0.1以外は /err に移動 (302でリダイレクト)
    return redirect('/err');
}

完成しました!

172.22.0.1はdocker経由でアクセスしたときの値です。

まとめ

ミドルウェアを使用することで、メインの処理を行う前に様々な処理を行うことができます。ユーザ権限を使用した制限やロギングといった、メインロジックとは少し離れた、複数のページで共通して使うような処理によく利用されます。

ミドルウェアを活用して、コントローラが肥大化しないようにしていきましょう!

laravel middleware thumb

役に立ったらシェアしよう!