Laravel - 23. ミドルウェア
Laravelの特徴的な仕組みの1つにミドルウェアがあります。ミドルウェアとはHTTPリクエストやレスポンスを透過的に処理する仕組みです。Laravelフレームワークの中でもデフォルトでいくつかのミドルウェアが実装されています。ミドルウェアはユーザーの認証処理やバリデーション、セッション管理など様々な用途に利用されています。ここではLaravelのミドルウェアの仕組みについて学習します。
ミドルウェアの管理
Laravelのミドルウェアと呼ばれるプログラムは app/Http/Kernel.php
ファイルで管理されています。テキストエディタで開くと次のように表示されるでしょう。
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
}
上記のように app/Http/Kernel.php
ファイルには様々なミドルウェアが定義されているのがわかります。また app/Http/Kernel.php
ファイルには4つの変数が定義されています。
変数名 | 意味 |
---|---|
$middleware |
ミドルウェア。すべてのルートに対して有効となる |
$middlewareGroups |
ミドルウェアグループ。 'web' 、 'api' などグループ化したルートに対応する |
$routeMiddleware |
ルートミドルウェア。特定のルートに対応する |
$middlewarePriority |
ミドルウェアプライオリティ。ミドルウェアの実行優先順序を決定する |
このようにミドルウェアはルーティングに合わせてカスタマイズできるようになっています。
ミドルウェアの作成
ここでは新たにコントローラの処理時間を計測する LoggingMiddleware
クラスを作成することにします。ミドルウェアの作成には php artisan make:middleware
コマンドを使います。
$ php artisan make:middleware ミドルウェア名
ミドルウェア名に LoggingMiddleware
と指定してコマンドを実行してみましょう。
$ php artisan make:middleware LoggingMiddleware
Middleware created successfully.
コマンドが成功すると app/Http/Middleware/LoggingMiddleware.php
ファイルが生成されます。
<?php
namespace App\Http\Middleware;
use Closure;
class LoggingMiddleware
{
public function handle($request, Closure $next)
{
return $next($request);
}
}
生成された LoggingMiddleware
クラスには handle
メソッドが定義されています。この handle
メソッドを実装してコントローラの処理時間を計測するようにします。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Carbon;
class LoggingMiddleware
{
public function handle($request, Closure $next)
{
$start = Carbon::now();
$response = $next($request);
$end = Carbon::now();
$diff = $end->diffInMilliseconds($start);
$path = $request->path();
Log::info($path . ": " . $diff . " ms");
return $response;
}
}
handle
メソッドは2つの引数を受け取ります。第1引数はミドルウェアの処理しているリクエストを示す $request
、第2引数は次のレイヤーのミドルウェア(あるいはコントローラ)を示す Closure
型の $next
です。
$response = $next($request);
上記のように $next
変数を関数として呼び出すことで、次のレイヤーのミドルウェア(終端の場合はコントローラ)を呼び出すことができます。
また handle
メソッドはコントローラのアクションメソッドの前処理・後処理として利用できます。具体的には response = $next($request);
より以前に記述しているコードは、アクションメソッド呼び出しの前処理として機能し、後に記述しているコードはアクションメソッド呼び出しの後処理として機能します。
今回のサンプルプログラムでは、アクションメソッドの前処理として $start = Carbon::now();
を呼び出しています。 Carbon
は日時操作のライブラリです。 use Illuminate\Support\Carbon;
によってプログラムの中で利用できるようになります。ここではコントローラのアクションメソッドを呼び出す前に現在日時を指定して Carbon
インスタンスを生成しています。
その後、 $response = $next($request);
を呼び出したあとに再度、Carbon
インスタンスを生成して処理時間を算出しています。
$end = Carbon::now();
$diff = $end->diffInMilliseconds($start);
$path = $request->path();
Log::info($path . ": " . $diff . " ms");
return $response;
2つの Carbon
インスタンスを使って $diff = $end->diffInMilliseconds($start);
とすることで処理時間(ms)を算出しています。それから Log::info($path . ": " . $diff . " ms");
とすることで、ログファイルにアクセスされたパスと処理時間を記録しています。また handle
メソッドは戻り値にレスポンスを示す $response
変数を返します。これによって次のレイヤーのミドルウェアへと処理が進みます。
続いて作成した LoggingMiddleware
クラスを app/Http/Kernel.php
に追加してみましょう。ここでは $middlewareGroups
変数( 'web'
グループ)と $middlewarePriority
変数の2箇所に LoggingMiddleware
を追記します。
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
# 省略...
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\LoggingMiddleware::class,
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
# 省略...
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
\App\Http\Middleware\LoggingMiddleware::class,
];
}
上記のように修正することで 'web'
グループに属するルートに LoggingMiddleware
が機能するようになります。また実行の優先順序は最後としているのでコントローラのアクションメソッドの呼び出しの前後で機能することになります。
ミドルウェアの定義情報は php artisan route:list
コマンドで確認できます。
$ php artisan route:list
+--------+----------+--------------+------+---------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+--------------+------+---------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | hello | | App\Http\Controllers\HelloController@index | web |
| | GET|HEAD | hello/create | | App\Http\Controllers\HelloController@create | web |
| | POST | hello/store | | App\Http\Controllers\HelloController@store | web |
+--------+----------+--------------+------+---------------------------------------------+--------------+
最終列の Middleware
に web
と表示されているルートに LoggingMiddleware
が適用されるようになります。
動作確認
それでは LoggingMiddleware
を追加したので、PHPのビルトインWebサーバを使ってWebアプリケーションを起動してみましょう。次のようにコマンドを入力します。
$ php artisan serve --host 0.0.0.0
Laravel development server started: http://0.0.0.0:8000
続いてブラウザを起動してWebアプリケーションにアクセスしてみましょう。
http://localhost:8000/hello
HelloController
の index
アクションの結果が表示されます。
もう一つアドレスバーから別のURLにアクセスしてみましょう。
http://localhost:8000/hello/create
この場合は HelloController
の create
アクションの結果が表示されます。
最後にログファイル( storage/logs/laravel.log
)を確認してみましょう。
[2020-09-07 23:14:31] local.INFO: hello: 130 ms
[2020-09-07 23:16:57] local.INFO: hello/create: 60 ms
ログファイルには2つのアクションメソッドの呼び出しにかかった処理時間が出力されているのがわかります。
まとめ
- ミドルウェアはHTTPリクエスト(レスポンス)を透過的に処理する仕組み
app/Http/Kernel.php
ファイルによってミドルウェアを管理するphp artisan make:middleware
コマンドでミドルウェアを作成できる