[Laravel]ミドルウェアの前処理と後処理


Laravelのミドルウェアの知識を整理すべくこの記事を書く。

ミドルウェアとは

ミドルウェアはリクエストに対してコントローラーより前やクライアントにレスポンスを返す前に行われる処理である。

今回気になったのは前処理だけでなく後処理もできるという点である。

ミドルウェアの作成

$ php artisan make:middleware TestMiddleware

Testmiddlewareの中身は以下のように記述する。

TestMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class TestMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $input = "Testミドルウェア";
        $request->merge(['content'=>$input]);
        return $next($request);
    }
}

コントローラは以下のような内容である。

TestController.php
class LaraController extends Controller
{
    public function index()
    {

        $item = [
            'content' => '入力してください',
        ];
        return view('index', $item);
    }
    public function post(Request $request)
    {
        $content = $request->content;
        $item = [
            'content' => $content . 'と入力しましたね'
        ];
        return view('index', $item);
    }
}

またルーティングは以下のようにする。

web.php
Route::get('/', [TestController::class, 'index']);
Route::post('/', [TestController::class, 'post'])->middleware(TestMiddleware::class);

ビューとなるindex.blade.php上では以下のようにリクエストにcontentというキーで入力したものを渡し、レスポンスでは$contentという値が表示されるようにする。

index.blade.php
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <h1>{{$content}}</h1>
  <form action="/" method="POST">
    @csrf
    <input type="text" name="content" >
    <input type="submit"  >
  </form>
</body>

</html>

そして、これで表示されたinput欄に文字を代入して送信し、「Testミドルウェア」と返ってきたらしっかりとミドルウェアが利用できていたことになる。
もちろん、Kernel.phpへのミドルウェアの登録は忘れずに。

検証

前処理

ここまでの処理は前処理を行なっていたことになる。つまり、送信のPOST処理に対してミドルウェア→コントローラの順で処理を行なっていたということである。

そのことを確かめるにはddメソッドを利用してみると良いだろう。
まず、ミドルウェアを以下のように二つのddを設置すると、それぞれ前半のddでは入力した値、後半の
$request->merge(['content'=>$input]); の下のddでは既に「Testミドルウェア」に書き換わっていることがわかるだろう。

TestMiddleware
class TestMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        dd($request);
        $input = "Testミドルウェア";
        $request->merge(['content'=>$input]);
        dd($request);
        return $next($request);
    }
}

このようにミドルウェアで書き換わってからコントローラに値が渡されている、これが前処理ということである。

後処理

次は後処理である。もし、コントローラの処理の後に値が書き換えられているとしたらコントローラの中ではcontentの値はinput欄に入力したものとなる。
まずは、ミドルウェアを書き換える。

TestMiddleware.php
class TestMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        dd($request);
        $response = $next($request);
        $input = "Testミドルウェア";
        $request->merge(['content'=>$input]);
        dd($request);
        return $request;
    }
}

次に、検証のため、コントローラーのpostアクションもddを加えて書き換える。

TestController.php
 public function post(Request $request)
    {
        dd($request);
        $content = $request->content;
        $item = [
            'content' => $content . 'と入力しましたね'
        ];
        return view('index', $item);
    }

この3つのddをコメントアウトしながら検証すると、最初に $response = $next($request); の上のdd、次にコントローラーのdd、最後に $request->merge(['content'=>$input]);の下のddが行われていることが分かった。

そして、値が切り替わったのは最後のddであったつまり、最後のddまではinputで入力した値であった。

これは $response = $next($request); 以降の処理はコントローラでinputの値を代入してから最後にミドルウェアでmergeして値を再代入しているということがわかった。つまり、後処理を行っていたということである。

なぜこのようになったのか?

後処理のミドルウェアで出てきた$response = $next($request) はレスポンスインスタンスである。つまり、$next($request) が実行されるとその時点で一度他のミドルウェアのハンドルまたはミドルウェアを呼び出す。しかし、なければ、アクションが呼び出されるのである。このアクションが終了した時点でページがレンダリングされるてレスポンスが生成される。この時のレスポンスがミドルウェアの $next($request) の$requestという引数として渡されるため、残りの後処理が行われるのである。
そのため、$request->contentの代入が最後に行われるので前処理と後処理の違いが出るのだ。