[TOC]
## 1、簡介
[HTTP](http://laravelacademy.org/tags/http "View all posts in HTTP")[中間件](http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6 "View all posts in 中間件")提供了一個便利的機制來[過濾](http://laravelacademy.org/tags/%e8%bf%87%e6%bb%a4 "View all posts in 過濾")進入應用的HTTP[請求](http://laravelacademy.org/tags/%e8%af%b7%e6%b1%82 "View all posts in 請求")。例如,[Lumen](http://laravelacademy.org/tags/lumen "View all posts in Lumen")包含了一個中間件來驗證用戶是否經過授權,如果用戶沒有經過授權,中間件會將用戶重定向到登錄頁面,否則如果用戶經過授權,中間件就會允許請求繼續往前進入下一步操作。
當然,除了認證之外,中間件還可以被用來處理更多其它任務。比如:CORS中間件可以用于為離開站點的響應添加合適的頭(跨域);日志中間件可以記錄所有進入站點的請求。
## 2、定義中間件
中間件通常都放在`app/Http/Middleware`目錄下。想要創建一個新的中間件,需要在新創建的中間件中重寫`handle`方法。在下面中間件中,我們只允許提供的age大于200的訪問[路由](http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1 "View all posts in 路由"),否則,我們將用戶重定向到主頁:
~~~
<?php
namespace App\Http\Middleware;
use Closure;
class OldMiddleware
{
/**
* 返回請求過濾器
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->input('age') <= 200) {
return redirect('home');
}
return $next($request);
}
}
~~~
正如你所看到的,如果`age<=200`,中間件會返回一個HTTP重定向到客戶端;否則,請求會被傳遞下去。將請求往下傳遞可以通過調用回調函數`$next`。
理解中間件的最好方式就是將中間件看做HTTP請求到達目標之前必須經過的“層”,每一層都會檢查請求甚至會完全拒絕它。
### 2.1 中間件之前/之后
一個中間件是否請求前還是請求后執行取決于中間件本身。比如,以下中間件會在請求處理前執行一些任務:
~~~
<?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;
}
}
~~~
## 3、注冊中間件
### 3.1 全局中間件
如果你想要中間件在每一個HTTP請求期間被執行,只需要將相應中間件類放到`bootstrap/app.php`文件的`$app->middleware()`調用中即可。
### 3.2 分配中間件到路由
如果你想要分配中間件到指定路由,首先應該在`bootstrap/app.php`文件中分配給該中間件一個簡寫的key,默認情況下,`$app->routeMiddleware()`方法包含了Lumen自帶的入口中間件,添加你自己的中間件只需要將其追加到后面并為其分配一個key:
~~~
$app->routeMiddleware([
'old' => 'App\Http\Middleware\OldMiddleware',]);
~~~
中間件在入口文件中被定義好了之后,可以在路由選項數組中使用`middleware`鍵來指定中間件:
~~~
$app->get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
~~~
## 4、中間件參數
中間件還可以接收額外的自定義參數,比如,如果應用需要在執行動作之前驗證認證用戶是否擁有指定的角色,可以創建一個`RoleMiddleware`來接收角色名作為額外參數。
額外的中間件參數會在`$next`參數之后傳入中間件:
~~~
<?php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
/**
* 運行請求過濾器
*
* @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);
}
}
~~~
中間件參數可以在定義路由時通過:分隔中間件名和參數名來指定,多個中間件參數可以通過逗號分隔:
~~~
$app->put('post/{id}', ['middleware' => 'role:editor', function ($id) {
//
}]);
~~~
## 5、中止中間件
有時候中間件可能需要在HTTP響應發送到瀏覽器之后做一些工作。比如,Lumen自帶的“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`方法將會接收請求和響應作為參數。一旦你定義了一個終結中間件,應該將其加入到入口文件的全局中間件列表中。