イントロダクション
ミドルウェアは、アプリケーションに入るHTTPリクエストを検査およびフィルタリングするための便利なメカニズムを提供します。たとえば、Laravelには、アプリケーションのユーザーが認証されていることを確認するミドルウェアが含まれています。ユーザーが認証されていない場合、ミドルウェアはユーザーをアプリケーションのログイン画面にリダイレクトします。逆に、ユーザーが認証されている場合、ミドルウェアはリクエストをアプリケーションへ進めることを許可します。
ミドルウェアを追加して、認証以外にもさまざまなタスクを実行できます。たとえば、ログミドルウェアなら、アプリケーションが受信したすべてのリクエストをログへ記録できるでしょう。Laravelフレームワークには、認証やCSRF保護用のミドルウェアなど、ミドルウェアがいくつか含まれています。これらのミドルウェアはすべて、app/Http/Middleware
ディレクトリにあります。
ミドルウェアの定義
新しいミドルウェアを作成するには、make:middleware
Artisanコマンドを使用します。
php artisan make:middleware EnsureTokenIsValid
このコマンドは、新しいEnsureTokenIsValid
クラスをapp/Http/Middleware
ディレクトリ内に配置します。例としてこのミドルウェアで、リクエストが供給するtoken
入力が、指定値と一致する場合にのみ、ルートへのアクセスを許可します。それ以外の場合は、ユーザーをhome
URIへリダイレクトしましょう。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureTokenIsValid
{
/**
* 受信リクエストの処理
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('home');
}
return $next($request);
}
}
ご覧のとおり、与えられたtoken
がシークレットトークンと一致しない場合、ミドルウェアはHTTPリダイレクトをクライアントに返します。それ以外の場合、リクエストはさらにアプリケーションに渡されます。リクエストをアプリケーションのより深いところに渡す(ミドルウェアが「パス」できるようにする)には、$request
を使用して$next
コールバックを呼び出す必要があります。
ミドルウェアは、HTTPリクエストがアプリケーションに到達する前に通過しなければならない一連の「レイヤー」として考えるのがベストです。各レイヤーはリクエストを検査したり、完全に拒否したりすることができます。
Note: すべてのミドルウェアはサービスコンテナを介して依存解決されるため、ミドルウェアのコンストラクター内で必要な依存関係をタイプヒントで指定できます。
ミドルウェアとレスポンス
もちろん、ミドルウェアはアプリケーションのより深部へリクエストの処理を委ねるその前後にあるタスクを実行できます。たとえば、次のミドルウェアは、リクエストがアプリケーションによって処理される前にいくつかのタスクを実行します。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class BeforeMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// アクションの実行…
return $next($request);
}
}
一方、このミドルウェアは、リクエストがアプリケーションによって処理された後にそのタスクを実行します。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class AfterMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// アクションの実行…
return $response;
}
}
ミドルウェアの登録
グローバルミドルウェア
アプリケーションへのすべてのHTTPリクエスト中であるミドルウェアを実行する場合は、app/Http/Kernel.php
クラスの$middleware
プロパティにそのミドルウェアクラスをリストします。
ルートに対するミドルウェアの指定
特定のルートにミドルウェアを割り当てたい場合は、ルート定義時にmiddleware
メソッドを呼び出してください。
use App\Http\Middleware\Authenticate;
Route::get('/profile', function () {
// ...
})->middleware(Authenticate::class);
ミドルウェア名の配列をmiddleware
メソッドへ渡し、ルートに複数のミドルウェアを割り当てることもできます。
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);
アプリケーションのapp/Http/Kernel.php
ファイルで、ミドルウェアにエイリアスを割り当てておくと便利です。デフォルトで、このクラスの$middlewareAliases
プロパティには、Laravelに含まれるミドルウェアのエントリーが含まれています。このリストに自分のミドルウェアを追加して、好きなエイリアスを割り当てられます。
// App\Http\Kernelクラス内…
protected $middlewareAliases = [
'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,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
HTTPカーネルでミドルウェアのエイリアスを定義したら、ルートにミドルウェアを割り当てるときに、そのエイリアスを使用できます。
Route::get('/profile', function () {
// ...
})->middleware('auth');
除外ミドルウェア
ミドルウェアをルートのグループに割り当てる場合、あるミドルウェアをグループ内の個々のルートに適用しないようにする必要が起きることもあります。これは、withoutMiddleware
メソッドを使用して実行できます。
use App\Http\Middleware\EnsureTokenIsValid;
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// …
});
Route::get('/profile', function () {
// …
})->withoutMiddleware([EnsureTokenIsValid::class]);
});
また、ルート定義のグループ全体から特定のミドルウェアのセットを除外することもできます。
use App\Http\Middleware\EnsureTokenIsValid;
Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/profile', function () {
// …
});
});
withoutMiddleware
メソッドはルートミドルウェアのみを削除でき、グローバルミドルウェアには適用されません。
ミドルウェアグループ
複数のミドルウェアを1つのキーにグループ化して、ルートへの割り当てを容易にしたい場合もあるでしょう。これは、HTTPカーネルの$middlewareGroups
プロパティを使用して実現可能です。
Laravelは、一般的にWebおよびAPIルートへ適用される可能性のあるミドルウェアをweb
およびapi
ミドルウェアグループへ予め定義しています。これらのミドルウェアグループは、アプリケーションのApp\Providers\RouteServiceProvider
サービスプロバイダによって、対応するweb
およびapi
ルートファイル内のルートに自動的に適用されることに注意してください。
/**
* アプリケーションのルートミドルウェアグループ
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
ミドルウェアグループは、個々のミドルウェアと同じ構文を使用して、ルートとコントローラアクションに割り当てることができます。繰り返しますが、ミドルウェアグループを使用すると、より便利に一度に多くのミドルウェアをルートに割り当てられます。
Route::get('/', function () {
// ...
})->middleware('web');
Route::middleware(['web'])->group(function () {
// ...
});
Note: 最初から、
web
およびapi
ミドルウェアグループは、App\Providers\RouteServiceProvider
によってアプリケーションの対応するroutes/web.php
およびroutes/api.php
ファイルへ自動的に適用されます。
ミドルウェアの順序
まれに、ミドルウェアを特定の順序で実行する必要があるでしょうが、ルートに割り当てられたときにはミドルウェアの順序を制御できません。この場合、app/Http/Kernel.php
ファイルの$middlewarePriority
プロパティを使用してミドルウェアの優先度を指定できます。このプロパティは、デフォルトではHTTPカーネルに存在していません。存在しない場合は、以下のデフォルト定義をコピーしてください。
/**
* ミドルウェアの優先順位でソートされたリスト
*
* これにより、非グローバルミドルウェアは常に指定する順序で実行されます。
*
* @var string[]
*/
protected $middlewarePriority = [
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
ミドルウェアのパラメータ
ミドルウェアは追加のパラメータを受け取ることもできます。たとえば、アプリケーションが特定のアクションを実行する前に、認証済みユーザーが特定の「役割り(role)」を持っていることを確認する必要がある場合、追加の引数として役割名を受け取るEnsureUserHasRole
ミドルウェアを作成できます。
追加のミドルウェアパラメータは、$next
引数の後にミドルウェアに渡されます。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* 受信リクエストの処理
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user()->hasRole($role)) {
// リダイレクト…
}
return $next($request);
}
}
ミドルウェアのパラメータはルート定義時に、ミドルウェア名とパラメータを「:」で区切って指定します。複数のパラメーターはコンマで区切る必要があります。
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor');
終了処理ミドルウェア
HTTPレスポンスがブラウザに送信された後、ミドルウェアが何らかの作業を行う必要がある場合があります。ミドルウェアでterminate
メソッドを定義し、WebサーバがFastCGIを使用している場合、レスポンスがブラウザに送信された後、terminate
メソッドが自動的に呼び出されます。
<?php
namespace Illuminate\Session\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class TerminatingMiddleware
{
/**
* 受信リクエストの処理
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
/**
* レスポンスがブラウザに送信された後にタスクを処理
*/
public function terminate(Request $request, Response $response): void
{
// …
}
}
terminate
メソッドは、リクエストとレスポンスの両方を受信する必要があります。終了処理ミドルウェアを定義したら、それをapp/Http/Kernel.php
ファイルのルートまたはグローバルミドルウェアのリストに追加する必要があります。
ミドルウェアでterminate
メソッドを呼び出すと、Laravelはサービスコンテナからミドルウェアの新しいインスタンスを依存解決します。handle
メソッドとterminate
メソッドが呼び出されたときに同じミドルウェアインスタンスを使用する場合は、コンテナのsingleton
メソッドを使用してミドルウェアをコンテナに登録します。通常、これはAppServiceProvider
のregister
メソッドで実行する必要があります。
use App\Http\Middleware\TerminatingMiddleware;
/**
* 全アプリケーションサービスの登録
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}