中間件
簡(jiǎn)介
HTTP 中間件為你的應(yīng)用提供了一種便利的機(jī)制去過濾客戶端的請(qǐng)求,比如說laravel中自帶的用來(lái)驗(yàn)證用戶是否已經(jīng)認(rèn)證的中間件宴杀,如果用戶的認(rèn)證沒有通過癣朗,那么他將被重定向到登錄視圖拾因。而如果用戶已經(jīng)通過認(rèn)證,那么他的請(qǐng)求就會(huì)被認(rèn)證中間件通過旷余,并將請(qǐng)求傳遞給應(yīng)用绢记。
中間件可以處理多種任務(wù),不僅僅只是用于驗(yàn)證用戶認(rèn)證正卧。比如你可以創(chuàng)建一個(gè)跨同源策略的中間件蠢熄,用來(lái)處理每個(gè)請(qǐng)求在被響應(yīng)前添加正確的響應(yīng)頭,你還可以創(chuàng)造一個(gè)日志中間件炉旷,在應(yīng)用被請(qǐng)求時(shí)優(yōu)先記錄下請(qǐng)求信息签孔。
Laravel框架本身提供了一些中間件叉讥,它們包括維護(hù)、認(rèn)證饥追、csrf保護(hù)图仓、session等中間件,這些中間件都被定義在app\Http\Middleware
目錄中但绕。
定義中間件
為了創(chuàng)建一個(gè)新的中間件救崔,你可以直接使用laravel提供的 make:middleware
artisan命令:
php artisan make:middleware AgeMiddleware
這條命令會(huì)在app\Http\Middleware
目錄下創(chuàng)建一個(gè)AgeMiddleware.php
文件。我們創(chuàng)造這么一個(gè)中間件捏顺,讓只有年齡大于200的路由通過:
<?php
namespace App\Http\Middleware;
use Closure;
class AgeMiddleware {
public function handle ($request, Closure $next) {
if ($request->get('age') > 200) {
return $next($request);
}
return redirect('home');
}
}
你可以看到六孵,如果請(qǐng)求中所提供的年齡小于等于200,請(qǐng)求將被直接返回一個(gè)重定向信息到客戶端,而如果年齡大于200幅骄,請(qǐng)求將被中間件繼續(xù)傳遞給應(yīng)用劫窒。為了在中間件中將請(qǐng)求轉(zhuǎn)交給應(yīng)用,你可以使用$next
回調(diào)函數(shù)拆座,并將$request
傳遞進(jìn)去烛亦。
你可以建立一系列的中間件來(lái)過濾客戶端的請(qǐng)求,這樣每一層中間件都可以檢查請(qǐng)求懂拾,如果通過煤禽,則將請(qǐng)求轉(zhuǎn)交到下一層,如果不通過則直接被駁回岖赋。
前行/后行 中間件
其實(shí)檬果,在中間件中不僅僅可以定義前行中間件,即在請(qǐng)求被轉(zhuǎn)交到應(yīng)用之前進(jìn)行處理的中間件唐断。
<?php
namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware {
public function handle ($request, Closure $next) {
// Perform action
return $next($request);
}
}
也可以定義優(yōu)先轉(zhuǎn)交請(qǐng)求給應(yīng)用的后執(zhí)行中間件选脊。
<? php
namespace App\Http\Middleware;
use Closure;
class AfterMiddleware {
public function handle ($request, Closure $next) {
$response = $next($request);
// Perform action
return $response;
}
}
注冊(cè)中間件
全局中間件
如果你需要一個(gè)可以過濾所有請(qǐng)求的中間件,那么你可以注冊(cè)一個(gè)全局中間件脸甘。你需要先定義好中間件恳啥,然后在app/Http/kernel.php
中的$middleware
數(shù)組屬性中進(jìn)行追加注冊(cè)。
分配中間件到路由
如果你想要分配中間件到特定的路由丹诀,那么你需要在app/Http/kernel.php
文件中$routeMiddleware
屬性中進(jìn)行追加注冊(cè)钝的,在這里你應(yīng)該定義一個(gè)短字符的別名,以便于你在路由分配時(shí)快速指定铆遭。
// Within App\Http\Kernel Class...
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \App\Http\Middleware\AuthenticateBasicAuth::class,
'gust' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \App\Http\Middleware\ThrottleRequest::class,
];
一旦你的中間件被注冊(cè)在了kernel
文件中硝桩,那么你就可以在定義路由時(shí)使用middleware
選項(xiàng)進(jìn)行中間件分配:
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
你可以通過這么做來(lái)分配多個(gè)中間件:
Route::get('/', ['middleware' => ['first', 'second'], function () {
//
}]);
當(dāng)然laravel也允許你通過鏈?zhǔn)椒椒?code>middleware去進(jìn)行中間件分配:
Route::get('/', function () {
//
})->middleware(['first', 'second']);
事實(shí)上,你也可以使用完全類名來(lái)進(jìn)行中間件分配:
use App\Http\Middleware\FooMiddleware;
Route::get('admin/profile', ['middleware' => FooMiddleware::class, function () {
//
}]);
中間件組
有時(shí)候你可能希望在分配路由時(shí)枚荣,可以通過一個(gè)別名來(lái)分配一系列的中間件到路由碗脊。你可以在kernel
文件中使用$middlewareGroups
屬性來(lái)進(jìn)行注冊(cè).
laravel自帶了web
和api
中間件組:
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,
],
'api' => [
'throttle:60,1',
'auth:api',
],
];
一旦注冊(cè)了中間件組,你可以使用相同語(yǔ)法去分配中間件組到路由:
Route::get('/', ['middleware' => ['web'], function () {
//
}]);
事實(shí)上橄妆,laravel自帶的web中間件組已經(jīng)被默認(rèn)啟用衙伶,所有在routes.php
中被定義的路由都被分配了此中間件祈坠。你可以在RouteServiceProvider.php
文件中進(jìn)行修改.
帶參數(shù)的中間件
中間件也可以接收額外的自定義參數(shù)。比如說你可能需要一個(gè)中間件來(lái)驗(yàn)證已認(rèn)證的用戶的權(quán)限問題矢劲。你可能需要傳遞一個(gè)角色名稱參數(shù)來(lái)執(zhí)行相應(yīng)的行為.那么你需要?jiǎng)?chuàng)建一個(gè)RoleMiddleware
來(lái)接收一個(gè)角色名稱作為額外的參數(shù).
額外的參數(shù)將會(huì)被傳遞在$next
參數(shù)之后:
<?php
namesapce App\Http\Middleware;
use Closure;
class RoleMiddleware {
public function handle ($request, Closure $next, $role) {
if (!$request->user()->hasRole($role)) {
// Redirect...
}
return $next($request);
}
}
帶參數(shù)的中間件在分配給路由時(shí)需要在中間件別名之后跟:
來(lái)分割別名和參數(shù)颁虐,多個(gè)參數(shù)需要使用,
分隔:
Route::post('post/{id}', ['middleware' => 'role:editor', function ($id) {
//
}]);
末端中間件
有時(shí)候你可能需要在響應(yīng)被發(fā)送到客戶端之后繼續(xù)處理一些任務(wù),比如說 session
中間件在laravel中就是響應(yīng)被發(fā)送出去之后才將session信息進(jìn)行存儲(chǔ)操作卧须。這時(shí)候你可以通過在中間件中添加terminate
方法來(lái)定義一個(gè)末端中間件:
<?php
namespace Illuminate\Session\Middleware;
use Closure;
class StartSession {
public function hanlde ($request, Closure $next) {
return $next($request);
}
public function terminate($request, $response) {
// Store the sessin data...
}
}
terminate
方法會(huì)接收請(qǐng)求和響應(yīng)另绩,一旦你定義了一個(gè)末端中間件,你應(yīng)該在kernel
文件中將其添加到全局中間件中.
每當(dāng)中間件中的terminate
方法被調(diào)用花嘶,laravel都會(huì)從服務(wù)容器中返回一個(gè)新的中間件實(shí)例笋籽,如果你想使用同一個(gè)實(shí)例,你應(yīng)該將其注冊(cè)在服務(wù)容器中并使用singleton
方法注冊(cè).