コンテンツにスキップ

ミドルウェア

はじめに

ミドルウェアは、アプリケーションに入る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リクエストがアプリケーションに到達する前に通過する必要がある一連の「レイヤー」として考えると最適です。各レイヤーはリクエストを検査し、完全に拒否することさえできます。

lightbulb

すべてのミドルウェアはサービスコンテナを介して解決されるため、ミドルウェアのコンストラクター内で必要な依存関係をタイプヒントできます。

ミドルウェアとレスポンス

もちろん、ミドルウェアはリクエストをアプリケーションのより深い部分に渡す前、または後にタスクを実行できます。たとえば、次のミドルウェアは、リクエストがアプリケーションによって処理される**前**に何らかのタスクを実行します

<?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のグローバルミドルウェアスタックを手動で管理する場合は、Laravelのデフォルトのグローバルミドルウェアスタックを`use`メソッドに提供できます。次に、必要に応じてデフォルトのミドルウェアスタックを調整できます

->withMiddleware(function (Middleware $middleware) {
$middleware->use([
\Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
// \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);

`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`メソッドはルートミドルウェアのみを削除でき、グローバルミドルウェアには適用されません。

ミドルウェアグループ

ルートへの割り当てを容易にするために、複数のミドルウェアを単一のキーでグループ化したい場合があります。これは、アプリケーションの`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ルートに適用する一般的なミドルウェアを含む、事前定義された`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,
]);
})
lightbulb

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

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

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