中間件爲過濾進入應用的 HTTP 請求提供了一套便利的機制。例如,Laravel 內置了一箇中間件來驗證用戶是否通過認證(如登陸),若是用戶沒有通過認證,中間件會將用戶重定向到登陸頁面,而若是用戶已經通過認證,中間件就會容許請求繼續往前進入下一步操做。php
固然,除了認證以外,中間件還能夠被用來處理不少其它任務。好比:CORS 中間件能夠用於爲離開站點的響應添加合適的頭(跨域);日誌中間件能夠記錄全部進入站點的請求,從而方便咱們構建系統日誌系統。laravel
Laravel 框架自帶了一些中間件,包括認證、CSRF 保護中間件等等。全部的中間件都位於 app/Http/Middleware
目錄下。web
要建立一個新的中間件,能夠經過 Artisan 命令 make:middleware
api
php artisan make:middleware CheckToken
這個命令會在 app/Http/Middleware
目錄下建立一個新的中間件類 CheckToken
,在這個中間件中,咱們只容許提供的 token
等於指定值 tlar.com 的請求訪問路由,不然,咱們將跳轉到百度:跨域
若是 token != 'tlar.com'
,中間件會返回一個 HTTP 重定向到百度;不然,請求會被傳遞下去。將請求往下傳遞能夠經過調用回調函數 $next
並傳入當前 $request
。數組
注:此時只是定義好了中間件的邏輯,要讓這個中間件生效,還要將其註冊到指定路由中瀏覽器
理解中間件的最好方式就是將中間件看作 HTTP 請求到達目標動做以前必須通過的「層」,每一層都會檢查請求而且能夠徹底拒絕它。session
注:全部的中間都是在服務容器中解析,因此你能夠在中間件的構造函數中類型提示任何依賴。app
一箇中間件是請求前仍是請求後執行取決於中間件自己。框架
好比,如下中間件會在請求處理前執行一些任務:
<?php namespace App\Http\Middleware; use Closure; class BeforeMiddleware { public function handle($request, Closure $next) { // 執行動做 return $next($request); } }
下面這個中間件則會在請求處理後執行其任務:
<?php namespace App\Http\Middleware; use Closure; class AfterMiddleware { public function handle($request, Closure $next) { $response = $next($request); // 執行動做 return $response; } }
中間件分三類,分別是全局中間件、中間件組和指定路由中間件:
若是你想要定義的中間件在每個 HTTP 請求時都被執行,只須要將相應的中間件類添加到 app/Http/Kernel.php
的數組屬性 $middleware
中便可:
除非真的須要,不然通常不會把業務級別的中間件放到全局中間件中。
若是你想要分配中間件到指定路由,首先應該在 app/Http/Kernel.php
文件中分配給該中間件一個 key
,默認狀況下,該類的 $routeMiddleware
屬性包含了 Laravel 自帶的中間件,要添加你本身的中間件,只須要將其追加到後面併爲其分配一個 key
,例如:
中間件在 HTTP Kernel 中被定義後,可使用 middleware
方法將其分配到路由:
Route::get('/', function () { return 'token'; })->middleware('token');
有時候你可能想要經過指定一個鍵名的方式將相關中間件分到同一個組裏面,這樣能夠更方便地將其分配到路由中,這能夠經過使用 HTTP Kernel 提供的 $middlewareGroups
屬性實現。
Laravel 自帶了開箱即用的 web
和 api
兩個中間件組,分別包含能夠應用到 Web 和 API 路由的通用中間件:
中間件組使用和分配單箇中間件一樣的語法被分配給路由和控制器動做。再次申明,中間件組的目的只是讓一次分配給路由多箇中間件的實現更加方便:
Route::get('/', function () { // })->middleware('web'); Route::group(['middleware' => ['web']], function () { // });
默認狀況下, RouteServiceProvider
自動將中間件組 web
應用到 routes/web.php
文件,將中間件組 api
應用到 routes/api.php
:
固然能夠本身設置本身的中間件組,以實現更靈活的中間件分配策略:
/** * 應用的中間件組. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'auth:api', ], #新增blog 'blog' => [ 'token', ] ];
修改 routes/web.php
下面的中間件分配方式:
Route::group(['middleware'=>['blog']],function(){ Route::get('/', function () { return view('welcome', ['name' => 'blog view']); }); Route::view('/view', 'welcome', ['website' => 'blog /view ']); });
這樣訪問 http://blog.tlar
和 http://blog.tlar/view
的時候都要帶上 token=tlar.com
參數,不然就會跳轉到百度。
在某些特殊場景下,你可能須要中間件按照特定順序執行,可是一旦中間件已經分配到指定路由就無法控制它們的執行順序了。
在這種狀況下,你能夠在 app/Http/Kernel.php
文件中經過 $middlewarePriority
屬性來指定中間件的優先級:
/** * The priority-sorted list of middleware. * * This forces non-global middleware to always be in the given order. * * @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, ];
中間件還能夠接收額外的自定義參數,例如,若是應用須要在執行給定動做以前驗證認證用戶是否擁有指定的角色,能夠建立一個 CheckRole
來接收角色名做爲額外參數。
額外的中間件參數會在 $next
參數以後傳入中間件:
<?php namespace App\Http\Middleware; use Closure; class CheckRole { /** * 處理輸入請求 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role * @return mixed * translator http://laravelacademy.org */ public function handle($request, Closure $next, $role) { if (! $request->user()->hasRole($role)) { // Redirect... } return $next($request); } }
中間件參數能夠在定義路由時經過 :
分隔中間件名和參數名來指定,多箇中間件參數能夠經過逗號分隔:
Route::put('post/{id}', function ($id) { // })->middleware('role:editor');
終端中間件,能夠理解爲一個善後的後臺處理中間件。有時候中間件可能須要在 HTTP 響應發送到瀏覽器以後作一些工做,好比,Laravel 內置的 session
中間件會在響應發送到瀏覽器以後將 Session 數據寫到存儲器中,爲了實現這個功能,須要定義一個終止中間件並添加 terminate
方法到這個中間件:
<?php namespace Illuminate\Session\Middleware; use Closure; class StartSession { public function handle($request, Closure $next) { return $next($request); } public function terminate($request, $response) { // 存儲session數據... } }
terminate
方法將會接收請求和響應做爲參數。定義了一個終端中間件以後,還須要將其加入到 app/Http/Kernel.php
文件的全局中間件列表中。
當調用中間件上的 terminate
方法時,Laravel 將會從服務容器中取出一個該中間件的新實例,若是你想要在調用 handle
和 terminate
方法時使用同一個中間件實例,則須要使用容器提供的 singleton
方法以單例的方式將該中間件註冊到容器中。