ミドルウェア¶
イントロダクション¶
ミドルウェアは、アプリケーションに入るHTTPリクエストを検査およびフィルタリングするための便利なメカニズムを提供します。たとえば、Laravelには、アプリケーションのユーザーが認証されているかどうかを確認するミドルウェアが含まれています。ユーザーが認証されていない場合、ミドルウェアはユーザーをアプリケーションのログイン画面にリダイレクトします。ただし、ユーザーが認証されている場合、ミドルウェアはリクエストがアプリケーションに進むことを許可します。
認証以外にも、さまざまなタスクを実行するために追加のミドルウェアを記述できます。たとえば、ロギングミドルウェアは、アプリケーションへのすべての受信リクエストをログに記録する場合があります。Laravelには、認証やCSRF保護のためのミドルウェアが含まれています。ただし、すべてのユーザー定義ミドルウェアは、通常、アプリケーションのapp/Http/Middleware
ディレクトリに配置されます。
ミドルウェアの定義¶
新しいミドルウェアを作成するには、make:middleware
Artisanコマンドを使用します。
このコマンドは、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
{
/**
* 受信リクエストを処理します。
*
* @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リクエストがアプリケーションに到達する前に通過しなければならない一連の「層」として想像するのが最善です。各層はリクエストを検査し、完全に拒否することもできます。
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リクエスト中にミドルウェアを実行したい場合は、アプリケーションの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\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
メソッドはルートミドルウェアのみを削除し、グローバルミドルウェアには適用されません。
ミドルウェアグループ¶
複数のミドルウェアを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ルートに適用するための事前定義された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,
]);
または、ミドルウェアを完全に削除することもできます。
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,
]);
})
Note
デフォルトでは、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
ファイルで定義されると、ルートにミドルウェアを割り当てる際にエイリアスを使用できます。
便宜上、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);
}
}
ミドルウェアパラメータは、ルートを定義する際にミドルウェア名とパラメータを:
で区切ることで指定できます。
複数のパラメータはカンマで区切ることができます。
終了可能なミドルウェア¶
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
ファイルのルートまたはグローバルミドルウェアのリストに追加する必要があります。
ミドルウェアのhandle
メソッドとterminate
メソッドが呼び出されるときに同じミドルウェアインスタンスを使用したい場合は、コンテナのsingleton
メソッドを使用してコンテナにミドルウェアを登録します。通常、これはAppServiceProvider
のregister
メソッドで行うべきです。
use App\Http\Middleware\TerminatingMiddleware;
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}