中間件
中間件
中間件
介紹
中間件提供了一種方便的機(jī)制過濾進(jìn)入應(yīng)用程序的 HTTP 請求。例如,Laravel 包含一個中間件,驗證您的應(yīng)用程序的用戶身份驗證。如果用戶未被認(rèn)證,中間件會將用戶重定向到登錄界面。然而,如果用戶通過身份驗證,中間件將進(jìn)一步允許請求到應(yīng)用程序中。
當(dāng)然,除了身份認(rèn)證以外,還可以編寫另外的中間件來執(zhí)行各種任務(wù)。例如:CORS 中間件可以負(fù)責(zé)為所有離開應(yīng)用的響應(yīng)添加合適的頭部信息;日志中間件可以記錄所有傳入應(yīng)用的請求。
Laravel 自帶了一些中間件,包括身份驗證、CSRF 保護(hù)等。所有這些中間件都位于 app/Http/Middleware
目錄。
定義中間件
通過運(yùn)行 make:middleware
Artisan 命令來創(chuàng)建新的中間件:
php artisan make:middleware CheckAge
該命令會在 app/Http/Middleware
目錄下創(chuàng)建一個新的 CheckAge
類,在這個中間件中,我們僅允許 age
參數(shù)大于 200
的請求對此路由進(jìn)行訪問,否則,將重定向到 home
<?php namespace App\Http\Middleware; use Closure;class CheckAge{ /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($request->age <= 200) { return redirect('home'); } return $next($request); } }
正如你所見,假如給定的 age
參數(shù)小于或等于 200
,這個中間件將返回一個 HTTP 重定向到客戶端;否則,請求將進(jìn)一步傳遞到應(yīng)用中。要讓請求繼續(xù)傳遞到應(yīng)用程序中(即允許「通過」中間件驗證的),只需使用 $request
作為參數(shù)去調(diào)用回調(diào)函數(shù) $next
。
最好將中間件想象為一系列 HTTP
請求,必須經(jīng)過才能進(jìn)入你應(yīng)用的「層」。每一層都會檢查請求(是否符合某些條件),(如果不符合)甚至可以(在請求訪問你的應(yīng)用之前)完全拒絕掉。
{提示} 所有的中間件都是通過 服務(wù)容器,因此,可以在你的中間件的構(gòu)造函數(shù)中鍵入你需要的任何依賴。
前置 & 后置中間件
中間件是在請求之前或者之后執(zhí)行,取決于中間件的本身。例如,下面的中間件將在應(yīng)用處理請求 之前 執(zhí)行某些任務(wù):
<?php namespace App\Http\Middleware; use Closure;class BeforeMiddleware{ public function handle($request, Closure $next) { // Perform action return $next($request); } }
然而,這個中間件是在應(yīng)用請求 之后執(zhí)行某些任務(wù):
<?php namespace App\Http\Middleware; use Closure; class AfterMiddleware{ public function handle($request, Closure $next) { $response = $next($request); // Perform action return $response; } }
注冊中間件
全局中間件
如果你希望中間件在應(yīng)用處理每個 HTTP 請求期間運(yùn)行。只需要在 app/Http/Kernel.php
中的 $middleware
屬性中列出這個中間件
為路由分配中間件
假設(shè)你下想為指定的路由分配中間件 ,首先應(yīng)該在 app/Http/Kernel.php
文件內(nèi)為該中間件分配一個鍵。默認(rèn)情況下,該類中的 $routeMiddleware
屬性下包含了 Laravel 內(nèi)置的中間件。若要加入自定義的中間件,只需把它附加到列表后并為其分配一個自定義鍵。例如:
// 在 App\Http\Kernel 類中... protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ];
一旦在 HTTP 內(nèi)核中定義好了中間件,就可以通過 middleware
方法將為路由分配中間件:
Route::get('admin/profile', function () { // })->middleware('auth');
你也可以為路由分配多個中間件:
Route::get('/', function () { // })->middleware('first', 'second');
分配中間件時,還可以傳遞完整的類名:
use App\Http\Middleware\CheckAge; Route::get('admin/profile', function () { // })->middleware(CheckAge::class);
中間組件
某些時候你可以希望使用一個鍵把多個中間件打包成一個組,方便將他們應(yīng)用到路由中去。你可以使用 Http 核心的 $middlewareGroups
屬性。
Laravel 內(nèi)置了開箱即用的 web
和 api
中間件組,其中包含你可能希望應(yīng)用于 Web UI 和 API 路由的常用中間件:
/** * 應(yīng)用程序的路由中間件組 * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ ' throttle:60,1', 'auth:api',], ];
中間件組可以使用于單個中間件相同的語法分配給路由和控制器操作。同樣,中間件使得一次將多個中間件分配給一個路由更加方便:
Route::get('/', function () { // })->middleware('web'); Route::group(['middleware' => ['web']], function () { // });
{提示}
RouteServiceProvider
默認(rèn)將web
中間件組自動應(yīng)用到routes/web.php
。
Sorting(排序) 中間件
很少情況下,你可能需要中間件以特定的順序執(zhí)行,但是當(dāng)它們被分配到路由時,你無法控制它們的順序。在這種情況下,可以使用 app/Http/Kernel.php
文件的 $middlewarePriority
屬性指定中間件優(yōu)先級:
/** * 中間件的優(yōu)先級排序列表 * * 將會強(qiáng)制非全局中間件始終保持給定的順序。 * * @var array */ protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\Authenticate::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class,];
中間件參數(shù)
中間件還可以接收其他參數(shù)。例如,如果你的應(yīng)用程序需要在執(zhí)行給定操作之前驗證用戶是否為給定的「角色」 ,你可以創(chuàng)建一個 CheckRole
中間件,由它來接收「角色」名稱作為附加參數(shù)。
附加的中間參數(shù)應(yīng)該在 $next
參數(shù)之后傳遞給中間件:
<?php namespace App\Http\Middleware; use Closure;class CheckRole{ /** * 處理傳入的參數(shù) * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role * @return mixed */ public function handle($request, Closure $next, $role) { if (! $request->user()->hasRole($role)) { // Redirect... } return $next($request); } }
定義路由時通過一個 :
來隔開中間件名稱和參數(shù)來指定中間件參數(shù)。多個參數(shù)就使用逗號分隔:
Route::put('post/{id}', function ($id) { // })->middleware('role:editor');
Terminable 中間件
有時,在準(zhǔn)備好 HTTP 響應(yīng)之后,中間件可能需要做一些工作。例如,Laravel 內(nèi)置的「session」 中間件會在完全準(zhǔn)備好響應(yīng)后將會話數(shù)據(jù)寫入存儲。如果你在中間件上定義了一個 terminate
方法,那么它將會在響應(yīng)準(zhǔn)備發(fā)送到瀏覽器之后自動調(diào)用。
<?php namespace Illuminate\Session\Middleware; use Closure; class StartSession{ public function handle($request, Closure $next) { return $next($request); } public function terminate($request, $response) { // Store the session data... } }
terminate
方法應(yīng)該同時接收請求和響應(yīng)。定義了這個中間件之后,別忘了將它添加到路由列表或者 app/Http/Kernel.php
文件的全局中間件中。
當(dāng)你在中間件上調(diào)用 terminate
方法的時候, Laravel 將從 服務(wù)容器 中解析出一個新的中間件實例。如果在調(diào)用 handle
和 terminate
方法的同時使用相同的中間件實例,請使用容器的 singleton
方法在容器中注冊中間件。