はじめに
ミドルウェアは、アプリケーションに入るHTTPリクエストを検査し、フィルタリングするための便利なメカニズムを提供します。たとえば、Laravelには、アプリケーションのユーザーが認証されていることを確認するミドルウェアが含まれています。ユーザーが認証されていない場合、ミドルウェアはユーザーをアプリケーションのログイン画面にリダイレクトします。しかし、ユーザーが認証されている場合、ミドルウェアはリクエストをアプリケーション内でさらに進めることを許可します。
追加のミドルウェアは、認証以外のさまざまなタスクを実行するために作成できます。たとえば、ロギングミドルウェアは、アプリケーションに入るすべてのリクエストをログに記録するかもしれません。Laravelには、認証やCSRF保護のためのミドルウェアを含むさまざまなミドルウェアが含まれていますが、すべてのユーザー定義のミドルウェアは通常、アプリケーションのapp/Http/Middleware
ディレクトリにあります。
ミドルウェアの定義
新しいミドルウェアを作成するには、make:middleware
Artisanコマンドを使用します:
php artisan make:middleware EnsureTokenIsValid
このコマンドは、app/Http/Middleware
ディレクトリ内に新しいEnsureTokenIsValid
クラスを配置します。このミドルウェアでは、提供されたtoken
入力が指定された値と一致する場合にのみ、ルートへのアクセスを許可します。そうでない場合、ユーザーを/home
URIにリダイレクトします:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureTokenIsValid
{
/**
* Handle an incoming request.
*
* @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リダイレクトを返します。そうでない場合、リクエストはアプリケーション内でさらに進められます。リクエストをアプリケーション内でさらに進めるには(ミドルウェアに「通過」させるために)、$next
コールバックを$request
で呼び出す必要があります。
ミドルウェアは、HTTPリクエストがアプリケーションに到達する前に通過しなければならない一連の「レイヤー」として考えるのが最適です。各レイヤーはリクエストを検査し、完全に拒否することさえできます。
すべてのミドルウェアはサービスコンテナを介して解決されるため、ミドルウェアのコンストラクタ内で必要な依存関係を型ヒントすることができます。
ミドルウェアとレスポンス
もちろん、ミドルウェアはリクエストをアプリケーション内でさらに進める前または後にタスクを実行できます。たとえば、次のミドルウェアは、リクエストがアプリケーションによって処理される前にいくつかのタスクを実行します:
<?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
{
// Perform action
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);
// Perform action
return $response;
}
}
ミドルウェアの登録
グローバルミドルウェア
アプリケーションへのすべてのHTTPリクエスト中にミドルウェアを実行したい場合は、アプリケーションのbootstrap/app.php
ファイルのグローバルミドルウェアスタックに追加できます:
use App\Http\Middleware\EnsureTokenIsValid;
->withMiddleware(function (Middleware $middleware) {
$middleware->append(EnsureTokenIsValid::class);
})
<a name="manually-managing-laravels-default-global-middleware"></a>
#### Laravelのデフォルトグローバルミドルウェアの手動管理
Laravelのグローバルミドルウェアスタックを手動で管理したい場合は、Laravelのデフォルトのグローバルミドルウェアスタックを`````use`````メソッドに提供できます。その後、必要に応じてデフォルトのミドルウェアスタックを調整できます:
``````php
->withMiddleware(function (Middleware $middleware) {
$middleware->use([
// \Illuminate\Http\Middleware\TrustHosts::class,
\Illuminate\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
]);
})
`
ルートへのミドルウェアの割り当て
特定のルートにミドルウェアを割り当てたい場合は、ルートを定義するときにmiddleware
メソッドを呼び出すことができます:
use App\Http\Middleware\EnsureTokenIsValid;
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);
``````php
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);
`
ミドルウェアの除外
ルートのグループにミドルウェアを割り当てるとき、グループ内の個々のルートにミドルウェアが適用されないようにする必要がある場合があります。これは、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 () {
// ...
});
});
<a name="middleware-groups"></a>
### ミドルウェアグループ
時には、複数のミドルウェアを単一のキーの下にグループ化して、ルートに割り当てやすくしたい場合があります。これは、アプリケーションの`````bootstrap/app.php`````ファイル内で`````appendToGroup`````メソッドを使用して達成できます:
``````php
use App\Http\Middleware\First;
use App\Http\Middleware\Second;
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('group-name', [
First::class,
Second::class,
]);
$middleware->prependToGroup('group-name', [
First::class,
Second::class,
]);
})
`
ミドルウェアグループは、個々のミドルウェアと同じ構文を使用してルートやコントローラーアクションに割り当てることができます:
Route::get('/', function () {
// ...
})->middleware('group-name');
Route::middleware(['group-name'])->group(function () {
// ...
});
Laravelのデフォルトミドルウェアグループ
Laravelには、WebおよびAPIルートに適用したい一般的なミドルウェアを含む、あらかじめ定義されたweb
およびapi
ミドルウェアグループが含まれています。Laravelは、これらのミドルウェアグループを対応するroutes/web.php
およびroutes/api.php
ファイルに自動的に適用します:
web ミドルウェアグループ |
---|
Illuminate\Cookie\Middleware\EncryptCookies |
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse |
Illuminate\Session\Middleware\StartSession |
Illuminate\View\Middleware\ShareErrorsFromSession |
Illuminate\Foundation\Http\Middleware\ValidateCsrfToken |
Illuminate\Routing\Middleware\SubstituteBindings |
api ミドルウェアグループ |
---|
Illuminate\Routing\Middleware\SubstituteBindings |
これらのグループにミドルウェアを追加または先頭に追加したい場合は、アプリケーションのbootstrap/app.php
ファイル内でweb
およびapi
メソッドを使用できます。web
およびapi
メソッドは、appendToGroup
メソッドの便利な代替手段です:
use App\Http\Middleware\EnsureTokenIsValid;
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
EnsureUserIsSubscribed::class,
]);
$middleware->api(prepend: [
EnsureTokenIsValid::class,
]);
})
Laravelのデフォルトミドルウェアグループのエントリの1つをカスタムミドルウェアに置き換えることもできます:
use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;
$middleware->web(replace: [
StartSession::class => StartCustomSession::class,
]);
または、ミドルウェアを完全に削除することもできます:
$middleware->web(remove: [
StartSession::class,
]);
Laravelのデフォルトミドルウェアグループの手動管理
Laravelのデフォルトweb
およびapi
ミドルウェアグループ内のすべてのミドルウェアを手動で管理したい場合は、グループを完全に再定義できます。以下の例では、web
およびapi
ミドルウェアグループをデフォルトのミドルウェアで定義し、必要に応じてカスタマイズできるようにします:
->withMiddleware(function (Middleware $middleware) {
$middleware->group('web', [
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
]);
$middleware->group('api', [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
// 'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
]);
})
デフォルトでは、web
およびapi
ミドルウェアグループは、bootstrap/app.php
ファイルによってアプリケーションの対応するroutes/web.php
およびroutes/api.php
ファイルに自動的に適用されます。
ミドルウェアエイリアス
アプリケーションのbootstrap/app.php
ファイル内でミドルウェアにエイリアスを割り当てることができます。ミドルウェアエイリアスを使用すると、特定のミドルウェアクラスの短いエイリアスを定義でき、特に長いクラス名のミドルウェアに便利です:
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class
]);
})
アプリケーションのbootstrap/app.php
ファイルでミドルウェアエイリアスが定義されると、ルートにミドルウェアを割り当てるときにエイリアスを使用できます:
Route::get('/profile', function () {
// ...
})->middleware('subscribed');
便利なことに、Laravelの組み込みミドルウェアのいくつかはデフォルトでエイリアスが付けられています。たとえば、auth
ミドルウェアはIlluminate\Auth\Middleware\Authenticate
ミドルウェアのエイリアスです。以下はデフォルトのミドルウェアエイリアスのリストです:
エイリアス | ミドルウェア |
---|---|
auth |
Illuminate\Auth\Middleware\Authenticate |
auth.basic |
Illuminate\Auth\Middleware\AuthenticateWithBasicAuth |
auth.session |
Illuminate\Session\Middleware\AuthenticateSession |
cache.headers |
Illuminate\Http\Middleware\SetCacheHeaders |
can |
Illuminate\Auth\Middleware\Authorize |
guest |
Illuminate\Auth\Middleware\RedirectIfAuthenticated |
password.confirm |
Illuminate\Auth\Middleware\RequirePassword |
precognitive |
Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests |
signed |
Illuminate\Routing\Middleware\ValidateSignature |
subscribed |
\Spark\Http\Middleware\VerifyBillableIsSubscribed |
throttle |
Illuminate\Routing\Middleware\ThrottleRequests または Illuminate\Routing\Middleware\ThrottleRequestsWithRedis |
verified |
Illuminate\Auth\Middleware\EnsureEmailIsVerified |
ミドルウェアのソート
まれに、ミドルウェアを特定の順序で実行する必要がありますが、ルートに割り当てるときにその順序を制御できない場合があります。このような状況では、アプリケーションのbootstrap/app.php
ファイル内でpriority
メソッドを使用してミドルウェアの優先順位を指定できます:
->withMiddleware(function (Middleware $middleware) {
$middleware->priority([
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Auth\Middleware\Authorize::class,
]);
})
ミドルウェアパラメータ
ミドルウェアは追加のパラメータを受け取ることもできます。たとえば、アプリケーションが認証されたユーザーが特定のアクションを実行する前に特定の「役割」を確認する必要がある場合、役割名を追加の引数として受け取るEnsureUserHasRole
ミドルウェアを作成できます。
追加のミドルウェアパラメータは、$next
引数の後にミドルウェアに渡されます:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* Handle an incoming request.
*
* @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)) {
// Redirect...
}
return $next($request);
}
}
ミドルウェアパラメータは、ルートを定義するときにミドルウェア名とパラメータを:
で区切ることで指定できます:
use App\Http\Middleware\EnsureUserHasRole;
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor');
複数のパラメータはカンマで区切ることができます:
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');
終了可能なミドルウェア
時には、ミドルウェアがHTTPレスポンスがブラウザに送信された後に作業を行う必要がある場合があります。ミドルウェアにterminate
メソッドを定義し、WebサーバーがFastCGIを使用している場合、レスポンスがブラウザに送信された後にterminate
メソッドが自動的に呼び出されます:
<?php
namespace Illuminate\Session\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class TerminatingMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
/**
* Handle tasks after the response has been sent to the browser.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}
`````terminate`````メソッドをミドルウェアで呼び出すと、Laravelは[サービスコンテナ](/read/laravel-11-x/89c648800d1f2464.md)からミドルウェアの新しいインスタンスを解決します。`````handle`````および`````terminate`````メソッドが呼び出されるときに同じミドルウェアインスタンスを使用したい場合は、コンテナの`````singleton`````メソッドを使用してミドルウェアをコンテナに登録します。通常、これは`````register`````メソッドの中で行うべきです:
``````php
use App\Http\Middleware\TerminatingMiddleware;
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}
`