メインコンテンツまでスキップ

ミドルウェア

はじめに

ミドルウェアは、アプリケーションに入力される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リダイレクトを返します。それ以外の場合は、リクエストはアプリケーション内でさらに進められます。リクエストをアプリケーション内でさらに進めるために(ミドルウェアを「通過」させるために)、$request を使用して $next コールバックを呼び出す必要があります。

ミドルウェアは、アプリケーションに到達する前に 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);
})

withMiddleware クロージャに提供される $middleware オブジェクトは Illuminate\Foundation\Configuration\Middleware のインスタンスであり、アプリケーションのルートに割り当てられたミドルウェアを管理します。append メソッドはミドルウェアをグローバルミドルウェアのリストの末尾に追加します。リストの先頭にミドルウェアを追加したい場合は、prepend メソッドを使用する必要があります。

Laravel のデフォルトグローバルミドルウェアの手動管理

Laravel のグローバルミドルウェアスタックを手動で管理したい場合は、use メソッドに Laravel のデフォルトのグローバルミドルウェアスタックを提供できます。その後、必要に応じてデフォルトのミドルウェアスタックを調整できます:

    ->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` メソッドを呼び出すことができます:

```php
use App\Http\Middleware\EnsureTokenIsValid;

Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);

ルートに複数のミドルウェアを割り当てるには、middleware メソッドにミドルウェア名の配列を渡すことができます:

    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 () {
// ...
});
});

withoutMiddleware メソッドはルートミドルウェアのみを削除し、グローバルミドルウェアには適用されません。

ミドルウェアグループ

時には、複数のミドルウェアを1つのキーの下にグループ化して、ルートに割り当てやすくすることがあります。アプリケーションの bootstrap/app.php ファイル内で appendToGroup メソッドを使用してこれを実現できます:

    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 ミドルウェアグループが事前定義されています。覚えておいてください、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` メソッドの便利な代替手段です:

```php
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 のデフォルトミドルウェアグループのエントリをカスタムミドルウェアで置き換えることもできます:

    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 ミドルウェアのエイリアスです。以下は、デフォルトのミドルウェアエイリアスのリストです:

AliasMiddleware
authIlluminate\Auth\Middleware\Authenticate
auth.basicIlluminate\Auth\Middleware\AuthenticateWithBasicAuth
auth.sessionIlluminate\Session\Middleware\AuthenticateSession
cache.headersIlluminate\Http\Middleware\SetCacheHeaders
canIlluminate\Auth\Middleware\Authorize
guestIlluminate\Auth\Middleware\RedirectIfAuthenticated
password.confirmIlluminate\Auth\Middleware\RequirePassword
precognitiveIlluminate\Foundation\Http\Middleware\HandlePrecognitiveRequests
signedIlluminate\Routing\Middleware\ValidateSignature
subscribed\Spark\Http\Middleware\VerifyBillableIsSubscribed
throttleIlluminate\Routing\Middleware\ThrottleRequestsまたはIlluminate\Routing\Middleware\ThrottleRequestsWithRedis
verifiedIlluminate\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);
}

}

ミドルウェアパラメータは、ミドルウェア名とパラメータを:で区切ってルートを定義する際に指定できます:

    Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor');

複数のパラメータは、カンマで区切ることができます:

    Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor,publisher');

終了可能ミドルウェア

時々、ミドルウェアはHTTPレスポンスがブラウザに送信された後に作業を行う必要があります。ミドルウェアにterminateメソッドを定義し、WebサーバーがFastCGIを使用している場合、terminateメソッドは自動的にブラウザにレスポンスが送信された後に呼び出されます:

terminateメソッドはリクエストとレスポンスの両方を受け取る必要があります。終了可能なミドルウェアを定義したら、アプリケーションのbootstrap/app.phpファイルにそのミドルウェアをルートのリストまたはグローバルミドルウェアに追加する必要があります。

ミドルウェアのterminateメソッドを呼び出すとき、Laravelはサービスコンテナからミドルウェアの新しいインスタンスを解決します。handleメソッドとterminateメソッドが呼び出されるときに同じミドルウェアインスタンスを使用したい場合は、コンテナのsingletonメソッドを使用してミドルウェアをコンテナに登録してください。通常、これはAppServiceProviderregisterメソッドで行う必要があります:

    use App\Http\Middleware\TerminatingMiddleware;

/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}