[TOC]
# 簡介
中間件為過濾進入應用的 HTTP 請求提供了一套便利的機制。例如,Laravel 內置了一個中間件來驗證用戶是否經過認證(如登錄),如果用戶沒有經過認證,中間件會將用戶重定向到登錄頁面,而如果用戶已經經過認證,中間件就會允許請求繼續往前進入下一步操作。
當然,除了認證之外,中間件還可以被用來處理很多其它任務。比如:CORS 中間件可以用于為離開站點的響應添加合適的頭(跨域);日志中間件可以記錄所有進入站點的請求,從而方便我們構建系統日志系統。
Laravel 框架自帶了一些中間件,包括認證、CSRF 保護中間件等等。所有的中間件都位于 `app/Http/Middleware` 目錄下。
# 定義中間件
要創建一個新的中間件,可以通過 Artisan 命令 `make:middleware`:
~~~
php artisan make:middleware CheckToken
~~~
這個命令會在 `app/Http/Middleware` 目錄下創建一個新的中間件類 `CheckToken`,在這個中間件中,我們只允許提供的 `token` 等于指定值 `laravelacademy.org` 的請求訪問路由,否則,我們將跳轉到 Laravel 學院網站:
:-: 
正如你所看到的,如果 `token != 'laravelacademy.org'`,中間件會返回一個 HTTP 重定向到 Laravel 學院;否則,請求會被傳遞下去。將請求往下傳遞可以通過調用回調函數 `$next` 并傳入當前 `$request`。
> 注:此時只是定義好了中間件的邏輯,要讓這個中間件生效,還要將其注冊到指定路由中,我們很快就會在下面的注冊中間件部分教你怎么做。
理解中間件的最好方式就是將中間件看做 HTTP 請求到達目標動作之前必須經過的“層”,每一層都會檢查請求并且可以完全拒絕它。
## 請求之前/之后的中間件
一個中間件是請求前還是請求后執行取決于中間件本身。比如,以下中間件會在請求處理前執行一些任務:
~~~
<?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`,例如:
~~~
// 在 App\Http\Kernel 類中...
/**
* 應用的路由中間件列表
*
* 這些中間件可以分配給路由組或者單個路由
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'token' => CheckToken::class
];
~~~
中間件在 HTTP Kernel 中被定義后,可以使用 `middleware` 方法將其分配到路由:
~~~
Route::get('/', function () {
//
})->middleware('token');
~~~
這樣,當我們在瀏覽器中訪問 `http://blog.test` 時就會跳到 `http://laravelacademy.org`,只有當訪問 `http://blog.test?token=laravelacademy.org` 時才能看到如下效果:
:-: 
可以使用數組分配多個中間件到路由:
~~~
Route::get('/', function () {
//
})->middleware('token', 'auth');
~~~
分配中間件的時候還可以傳遞完整的類名(不過不推薦這種方式):
~~~
use App\Http\Middleware\CheckToken;
Route::get('admin/profile', function () {
//
})->middleware(CheckToken::class);
~~~
## 中間件組
有時候你可能想要通過指定一個鍵名的方式將相關中間件分到同一個組里面,這樣可以更方便地將其分配到路由中,這可以通過使用 HTTP Kernel 提供的 `$middlewareGroups` 屬性實現。
Laravel 自帶了開箱即用的 web 和 api 兩個中間件組,分別包含可以應用到 `Web` 和 `API` 路由的通用中間件:
~~~
/**
* 應用的中間件組
*
* @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` 自動將中間件組 `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' => [
'token',
]
];
~~~
我們修改 `routes/web.php` 下面的中間件分配方式:
~~~
Route::group(['middleware'=>['blog']],function(){
Route::get('/', function () {
return view('welcome', ['website' => 'Laravel']);
});
Route::view('/view', 'welcome', ['website' => 'Laravel學院']);
});
~~~
這樣我們訪問 `http://blog.test` 和 `http://blog.test/view` 的時候都要帶上 `token=laravelacademy.org` 參數,否則就會跳轉到 Laravel 學院網站。
# 中間件參數
中間件還可以接收額外的自定義參數,例如,如果應用需要在執行給定動作之前驗證認證用戶是否擁有指定的角色,可以創建一個 `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` 方法以單例的方式將該中間件注冊到容器中。關于服務容器我們就會在后面講到,暫時不深入展開了。
- 序言
- 新版特性
- 快速入門
- 升級指南
- 貢獻指南
- API文檔
- 安裝配置
- 目錄結構
- Homestead
- Valet
- 部署
- 核心概念
- 請求生命周期
- 服務容器
- 服務提供者
- 門面(Facades)
- 契約(Contracts)
- 框架基礎
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- 生成 URL
- Session
- 驗證
- 錯誤處理
- 日志
- 前端開發
- Blade 模板
- 本地化
- 前端腳手架
- 編譯前端資源(Laravel Mix)
- 安全系列
- 登錄認證
- API 認證
- 授權
- 加密
- 哈希
- 重置密碼
- 進階系列
- Artisan 控制臺
- 集合
- 廣播
- 緩存
- 事件
- 文件存儲
- 輔助函數
- 郵件
- 通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫操作
- 快速入門
- 查詢構建器
- 分頁
- 遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 關聯關系
- 集合
- 訪問器 & 修改器
- API 資源類
- 序列化
- 應用測試
- 快速入門
- HTTP 測試
- 瀏覽器測試
- 數據庫測試
- 模擬
- 官方擴展包
- Cashier(訂閱支付解決方案)
- Envoy(遠程操作解決方案)
- Horizon(隊列系統解決方案)
- Passport(API 認證解決方案)
- Scout(全文搜索解決方案)
- Socialite(第三方登錄解決方案)
- 相關下載
- Laravel 5.6 中文文檔離線版
- Laravel 5.6 一鍵安裝包