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