翻譯進度
28.96% 已翻譯
更新時間:
2024年6月30日 上午8:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

中介軟體 - Middleware

簡介

Middleware 提供了一個機制,可檢驗與過濾進入應用程式的 HTTP Request。舉例來說,Laravel 中包含了一個可以認證使用者是否已登入的 Middleware。若使用者未登入,該 Middleware 會將使用者重新導向回登入畫面。不過,若使用者已登入,這個 Middleware 就會讓 Request 進一步進入程式中處理。

Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. A variety of middleware are included in Laravel, including middleware for authentication and CSRF protection; however, all user-defined middleware are typically located in your application's app/Http/Middleware directory.

定義 Middleware

若要建立新的 Middleware,請使用 make:middleware Artisan 指令:

1php artisan make:middleware EnsureTokenIsValid
1php artisan make:middleware EnsureTokenIsValid

該指令會在 app/Http/Middleware 目錄中放置一個新的 EnsureTokenIsValid 類別。在這個 Middleware 中,我們要只在提供的 token 符合特定的值時才允許存取該 Route。token 不符合時,會將使用者重新導向回到 home URI:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class EnsureTokenIsValid
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next): Response
17 {
18 if ($request->input('token') !== 'my-secret-token') {
19 return redirect('home');
20 }
21 
22 return $next($request);
23 }
24}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class EnsureTokenIsValid
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next): Response
17 {
18 if ($request->input('token') !== 'my-secret-token') {
19 return redirect('home');
20 }
21 
22 return $next($request);
23 }
24}

就像我們可以看到的一樣,若給定的 token 不符合我們的私密權杖 (Secret Token),則這個 Middleware 會回傳一個 HTTP Redirect 給用戶端。token 符合時,這個 Request 就會進一步地傳給我們的程式。若要將 Request 進一步傳進我們的應用程式中 (即,讓 Middleware「通過 - Pass」),應以 $request 呼叫 $next 回呼。

最好想像成我們有「一層又一層」的 Middleware。HTTP Request 必須通過每一層的 Middleware,最後才能進入你的應用程式中。每一層 Middleware 都可以檢查 Request 的內容,甚至還能完全拒絕 Request。

lightbulb

所有的 Middleware 都會經過 [Service Container] 解析,因此我們可以在 Middleware 的 Constructor (建構函式) 上型別提示 (Type-Hint) 任何需要的相依性。

Middleware and Responses

當然,Middleware 可以在將 Request 傳入應用程式的前後執行。舉例來說,下列 Middleware 會在 Request 被程式處理 之後 進行一些任務:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class BeforeMiddleware
10{
11 public function handle(Request $request, Closure $next): Response
12 {
13 // Perform action
14 
15 return $next($request);
16 }
17}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class BeforeMiddleware
10{
11 public function handle(Request $request, Closure $next): Response
12 {
13 // Perform action
14 
15 return $next($request);
16 }
17}

不過,這個 Middleware 會在 Request 被程式處理 之後 才進行其任務:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class AfterMiddleware
10{
11 public function handle(Request $request, Closure $next): Response
12 {
13 $response = $next($request);
14 
15 // Perform action
16 
17 return $response;
18 }
19}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class AfterMiddleware
10{
11 public function handle(Request $request, Closure $next): Response
12 {
13 $response = $next($request);
14 
15 // Perform action
16 
17 return $response;
18 }
19}

註冊 Middleware

全域 Middleware

If you want a middleware to run during every HTTP request to your application, you may append it to the global middleware stack in your application's bootstrap/app.php file:

1use App\Http\Middleware\EnsureTokenIsValid;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->append(EnsureTokenIsValid::class);
5})
1use App\Http\Middleware\EnsureTokenIsValid;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->append(EnsureTokenIsValid::class);
5})

The $middleware object provided to the withMiddleware closure is an instance of Illuminate\Foundation\Configuration\Middleware and is responsible for managing the middleware assigned to your application's routes. The append method adds the middleware to the end of the list of global middleware. If you would like to add a middleware to the beginning of the list, you should use the prepend method.

Manually Managing Laravel's Default Global Middleware

If you would like to manage Laravel's global middleware stack manually, you may provide Laravel's default stack of global middleware to the use method. Then, you may adjust the default middleware stack as necessary:

1->withMiddleware(function (Middleware $middleware) {
2 $middleware->use([
3 // \Illuminate\Http\Middleware\TrustHosts::class,
4 \Illuminate\Http\Middleware\TrustProxies::class,
5 \Illuminate\Http\Middleware\HandleCors::class,
6 \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
7 \Illuminate\Http\Middleware\ValidatePostSize::class,
8 \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
9 \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
10 ]);
11})
1->withMiddleware(function (Middleware $middleware) {
2 $middleware->use([
3 // \Illuminate\Http\Middleware\TrustHosts::class,
4 \Illuminate\Http\Middleware\TrustProxies::class,
5 \Illuminate\Http\Middleware\HandleCors::class,
6 \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
7 \Illuminate\Http\Middleware\ValidatePostSize::class,
8 \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
9 \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
10 ]);
11})

Assigning Middleware to Routes

若想將 Middleware 指定到特定的 Route 中,可以在定義 Route 時呼叫 middleware 方法:

1use App\Http\Middleware\EnsureTokenIsValid;
2 
3Route::get('/profile', function () {
4 // ...
5})->middleware(EnsureTokenIsValid::class);
1use App\Http\Middleware\EnsureTokenIsValid;
2 
3Route::get('/profile', function () {
4 // ...
5})->middleware(EnsureTokenIsValid::class);

也可以傳入一組 Middleware 陣列給 middleware 方法來指派多個 Middleware 給 Route:

1Route::get('/', function () {
2 // ...
3})->middleware([First::class, Second::class]);
1Route::get('/', function () {
2 // ...
3})->middleware([First::class, Second::class]);

排除 Middleware

當我們將 Middleware 指派給 Route 群組時,我們有時候會需要讓某個 Middleware 不要被套用到群組中的個別 Route 上。我們可以使用 withoutMiddleware 方法來完成:

1use App\Http\Middleware\EnsureTokenIsValid;
2 
3Route::middleware([EnsureTokenIsValid::class])->group(function () {
4 Route::get('/', function () {
5 // ...
6 });
7 
8 Route::get('/profile', function () {
9 // ...
10 })->withoutMiddleware([EnsureTokenIsValid::class]);
11});
1use App\Http\Middleware\EnsureTokenIsValid;
2 
3Route::middleware([EnsureTokenIsValid::class])->group(function () {
4 Route::get('/', function () {
5 // ...
6 });
7 
8 Route::get('/profile', function () {
9 // ...
10 })->withoutMiddleware([EnsureTokenIsValid::class]);
11});

也可以將一組 Middleware 從整個 Route 群組定義中排除:

1use App\Http\Middleware\EnsureTokenIsValid;
2 
3Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
4 Route::get('/profile', function () {
5 // ...
6 });
7});
1use App\Http\Middleware\EnsureTokenIsValid;
2 
3Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
4 Route::get('/profile', function () {
5 // ...
6 });
7});

withoutMiddleware 方法只能移除 Route Middleware,不能移除全域 Middleware

Middleware 群組

Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the appendToGroup method within your application's bootstrap/app.php file:

1use App\Http\Middleware\First;
2use App\Http\Middleware\Second;
3 
4->withMiddleware(function (Middleware $middleware) {
5 $middleware->appendToGroup('group-name', [
6 First::class,
7 Second::class,
8 ]);
9 
10 $middleware->prependToGroup('group-name', [
11 First::class,
12 Second::class,
13 ]);
14})
1use App\Http\Middleware\First;
2use App\Http\Middleware\Second;
3 
4->withMiddleware(function (Middleware $middleware) {
5 $middleware->appendToGroup('group-name', [
6 First::class,
7 Second::class,
8 ]);
9 
10 $middleware->prependToGroup('group-name', [
11 First::class,
12 Second::class,
13 ]);
14})

Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware:

1Route::get('/', function () {
2 // ...
3})->middleware('group-name');
4 
5Route::middleware(['group-name'])->group(function () {
6 // ...
7});
1Route::get('/', function () {
2 // ...
3})->middleware('group-name');
4 
5Route::middleware(['group-name'])->group(function () {
6 // ...
7});

Laravel's Default Middleware Groups

Laravel includes predefined web and api middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, Laravel automatically applies these middleware groups to the corresponding routes/web.php and routes/api.php files:

The web Middleware Group
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
The api Middleware Group
Illuminate\Routing\Middleware\SubstituteBindings

If you would like to append or prepend middleware to these groups, you may use the web and api methods within your application's bootstrap/app.php file. The web and api methods are convenient alternatives to the appendToGroup method:

1use App\Http\Middleware\EnsureTokenIsValid;
2use App\Http\Middleware\EnsureUserIsSubscribed;
3 
4->withMiddleware(function (Middleware $middleware) {
5 $middleware->web(append: [
6 EnsureUserIsSubscribed::class,
7 ]);
8 
9 $middleware->api(prepend: [
10 EnsureTokenIsValid::class,
11 ]);
12})
1use App\Http\Middleware\EnsureTokenIsValid;
2use App\Http\Middleware\EnsureUserIsSubscribed;
3 
4->withMiddleware(function (Middleware $middleware) {
5 $middleware->web(append: [
6 EnsureUserIsSubscribed::class,
7 ]);
8 
9 $middleware->api(prepend: [
10 EnsureTokenIsValid::class,
11 ]);
12})

You may even replace one of Laravel's default middleware group entries with a custom middleware of your own:

1use App\Http\Middleware\StartCustomSession;
2use Illuminate\Session\Middleware\StartSession;
3 
4$middleware->web(replace: [
5 StartSession::class => StartCustomSession::class,
6]);
1use App\Http\Middleware\StartCustomSession;
2use Illuminate\Session\Middleware\StartSession;
3 
4$middleware->web(replace: [
5 StartSession::class => StartCustomSession::class,
6]);

Or, you may remove a middleware entirely:

1$middleware->web(remove: [
2 StartSession::class,
3]);
1$middleware->web(remove: [
2 StartSession::class,
3]);

Manually Managing Laravel's Default Middleware Groups

If you would like to manually manage all of the middleware within Laravel's default web and api middleware groups, you may redefine the groups entirely. The example below will define the web and api middleware groups with their default middleware, allowing you to customize them as necessary:

1->withMiddleware(function (Middleware $middleware) {
2 $middleware->group('web', [
3 \Illuminate\Cookie\Middleware\EncryptCookies::class,
4 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
5 \Illuminate\Session\Middleware\StartSession::class,
6 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
7 \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
8 \Illuminate\Routing\Middleware\SubstituteBindings::class,
9 // \Illuminate\Session\Middleware\AuthenticateSession::class,
10 ]);
11 
12 $middleware->group('api', [
13 // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
14 // 'throttle:api',
15 \Illuminate\Routing\Middleware\SubstituteBindings::class,
16 ]);
17})
1->withMiddleware(function (Middleware $middleware) {
2 $middleware->group('web', [
3 \Illuminate\Cookie\Middleware\EncryptCookies::class,
4 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
5 \Illuminate\Session\Middleware\StartSession::class,
6 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
7 \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
8 \Illuminate\Routing\Middleware\SubstituteBindings::class,
9 // \Illuminate\Session\Middleware\AuthenticateSession::class,
10 ]);
11 
12 $middleware->group('api', [
13 // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
14 // 'throttle:api',
15 \Illuminate\Routing\Middleware\SubstituteBindings::class,
16 ]);
17})
lightbulb

By default, the web and api middleware groups are automatically applied to your application's corresponding routes/web.php and routes/api.php files by the bootstrap/app.php file.

Middleware Aliases

You may assign aliases to middleware in your application's bootstrap/app.php file. Middleware aliases allows you to define a short alias for a given middleware class, which can be especially useful for middleware with long class names:

1use App\Http\Middleware\EnsureUserIsSubscribed;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->alias([
5 'subscribed' => EnsureUserIsSubscribed::class
6 ]);
7})
1use App\Http\Middleware\EnsureUserIsSubscribed;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->alias([
5 'subscribed' => EnsureUserIsSubscribed::class
6 ]);
7})

Once the middleware alias has been defined in your application's bootstrap/app.php file, you may use the alias when assigning the middleware to routes:

1Route::get('/profile', function () {
2 // ...
3})->middleware('subscribed');
1Route::get('/profile', function () {
2 // ...
3})->middleware('subscribed');

For convenience, some of Laravel's built-in middleware are aliased by default. For example, the auth middleware is an alias for the Illuminate\Auth\Middleware\Authenticate middleware. Below is a list of the default middleware aliases:

Alias 中介軟體 - Middleware
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 or Illuminate\Routing\Middleware\ThrottleRequestsWithRedis
verified Illuminate\Auth\Middleware\EnsureEmailIsVerified

排序 Middleware

Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In these situations, you may specify your middleware priority using the priority method in your application's bootstrap/app.php file:

1->withMiddleware(function (Middleware $middleware) {
2 $middleware->priority([
3 \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
4 \Illuminate\Cookie\Middleware\EncryptCookies::class,
5 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
6 \Illuminate\Session\Middleware\StartSession::class,
7 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
8 \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
9 \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
10 \Illuminate\Routing\Middleware\ThrottleRequests::class,
11 \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
12 \Illuminate\Routing\Middleware\SubstituteBindings::class,
13 \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
14 \Illuminate\Auth\Middleware\Authorize::class,
15 ]);
16})
1->withMiddleware(function (Middleware $middleware) {
2 $middleware->priority([
3 \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
4 \Illuminate\Cookie\Middleware\EncryptCookies::class,
5 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
6 \Illuminate\Session\Middleware\StartSession::class,
7 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
8 \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
9 \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
10 \Illuminate\Routing\Middleware\ThrottleRequests::class,
11 \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
12 \Illuminate\Routing\Middleware\SubstituteBindings::class,
13 \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
14 \Illuminate\Auth\Middleware\Authorize::class,
15 ]);
16})

Middleware 參數

Middleware 也可以接收額外的參數。舉例來說,若你的程式需要在執行給定動作前認證登入的使用者是否有給定的「職位 (Role)」,則我們可以先建立一個 EnsureUserHasRole Middleware,讓該 Middleware 接收一個職位名稱來作為其額外的引數。

額外的 Middleware 引數會被放在 $next 引數之後傳遞給 Middleware:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class EnsureUserHasRole
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next, string $role): Response
17 {
18 if (! $request->user()->hasRole($role)) {
19 // Redirect...
20 }
21 
22 return $next($request);
23 }
24 
25}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class EnsureUserHasRole
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next, string $role): Response
17 {
18 if (! $request->user()->hasRole($role)) {
19 // Redirect...
20 }
21 
22 return $next($request);
23 }
24 
25}

Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a ::

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

Multiple parameters may be delimited by commas:

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

可終止的 Middleware

有時候,某個 Middleware 可能需要在 HTTP Response 被傳送到瀏覽器後才進行某些動作。若我們在 Middleware 上定義一個 terminate 方法,且網頁伺服器 (Web Server) 使用 FastCGI,則會在 Response 傳送給瀏覽器後會自動呼叫 terminate 方法:

1<?php
2 
3namespace Illuminate\Session\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class TerminatingMiddleware
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next): Response
17 {
18 return $next($request);
19 }
20 
21 /**
22 * Handle tasks after the response has been sent to the browser.
23 */
24 public function terminate(Request $request, Response $response): void
25 {
26 // ...
27 }
28}
1<?php
2 
3namespace Illuminate\Session\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class TerminatingMiddleware
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next): Response
17 {
18 return $next($request);
19 }
20 
21 /**
22 * Handle tasks after the response has been sent to the browser.
23 */
24 public function terminate(Request $request, Response $response): void
25 {
26 // ...
27 }
28}

The terminate method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in your application's bootstrap/app.php file.

呼叫 Middleware 上的 terminate 方法時,Laravel 會從 [Service Container] 中解析出這個 Middleware 的新實體。若想讓 handleterminate 都在同一個 Middleware 實體上呼叫的話,請使用 Container 的 singleton 方法來想 Container 註冊這個 Middleware。一般來說,這個註冊應在 AppServiceProviderregister 方法中進行:

1use App\Http\Middleware\TerminatingMiddleware;
2 
3/**
4 * Register any application services.
5 */
6public function register(): void
7{
8 $this->app->singleton(TerminatingMiddleware::class);
9}
1use App\Http\Middleware\TerminatingMiddleware;
2 
3/**
4 * Register any application services.
5 */
6public function register(): void
7{
8 $this->app->singleton(TerminatingMiddleware::class);
9}
翻譯進度
28.96% 已翻譯
更新時間:
2024年6月30日 上午8:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.