[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 請求")提供了一套便利的機制。例如,[Laravel](http://laravelacademy.org/tags/laravel "View all posts in Laravel")?內置了一個中間件來驗證用戶是否經過授權,如果用戶沒有經過授權,中間件會將用戶重定向到登錄頁面,否則如果用戶經過授權,中間件就會允許請求繼續往前進入下一步操作。
當然,除了認證之外,中間件還可以被用來處理更多其它任務。比如:CORS 中間件可以用于為離開站點的響應添加合適的頭(跨域);日志中間件可以記錄所有進入站點的請求。
Laravel框架自帶了一些中間件,包括維護模式、認證、CSRF 保護中間件等等。所有的中間件都位于`app/Http/Middleware`?目錄。
### **2、定義中間件**
要創建一個新的中間件,可以通過 Artisan 命令?`make:middleware`:
~~~
php artisan make:middleware OldMiddleware
~~~
這個命令會在?`app/Http/Middleware`?目錄下創建一個新的中間件類?`OldMiddleware`,在這個中間件中,我們只允許提供的?`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`?并傳入 ?`$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;
}
}
~~~
### **3、注冊中間件**
#### **全局中間件**
如果你想要中間件在每一個 HTTP 請求期間被執行,只需要將相應的中間件類設置到?`app/Http/Kernel.php`?的數組屬性?`$middleware`?中即可。
#### **分配中間件到路由**
如果你想要分配中間件到指定路由,首先應該在?`app/Http/Kernel.php`?文件中分配給該中間件一個簡寫的 key,默認情況下,該類的?`$routeMiddleware`?屬性包含了 Laravel 內置的入口中間件,添加你自己的中間件只需要將其追加到后面并為其分配一個 key:
~~~
// 在 App\Http\Kernel 里中
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
~~~
中間件在 HTTP Kernel 中被定義后,可以在路由選項數組中使用?`$middleware`?鍵來指定該中間件:
~~~
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
~~~
使用數組分配多個中間件到路由:
~~~
Route::get('/', ['middleware' => ['first', 'second'], function () {
//
}]);
~~~
除了使用數組外,還可以使用?`middleware`?方法鏈的方式定義路由:
~~~
Route::get('/', function () {
//
})->middleware(['first', 'second']);
~~~
#### **[中間件組](http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6%e7%bb%84 "View all posts in 中間件組")**
有時候你可能想要通過指定一個鍵名的方式將相關中間件分到一個組里面,從而更方便將其分配到路由中,這可以通過使用 HTTP Kernel 的?`$middlewareGroups`??實現。
Laravel 自帶了開箱即用的?`web`?和?`api`?兩個中間件組以包含可以應用到 Web UI 和 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,
],
'api' => [
'throttle:60,1',
'auth:api',
],
];
~~~
中間件組可以被分配給路由和控制器動作,使用和單個中間件分配同樣的語法。再次申明,中間件組的目的只是讓一次分配給路由多個中間件的實現更加簡單:
~~~
Route::group(['middleware' => ['web']], function () {
//
});
~~~
### **4、[中間件參數](http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6%e5%8f%82%e6%95%b0 "View all posts in 中間件參數")**
中間件還可以接收額外的自定義參數,例如,如果應用需要在執行給定動作之前驗證認證用戶是否擁有指定的角色,可以創建一個?`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);
}
}
~~~
中間件參數可以在定義路由時通過:分隔中間件名和參數名來指定,多個中間件參數可以通過逗號分隔:
~~~
Route::put('post/{id}', ['middleware' => 'role:editor', function ($id) {
//
}]);
~~~
### **5、可終止的中間件**
有時候中間件可能需要在 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`?方法將會接收請求和響應作為參數。一旦你定義了一個可終止的中間件,應該將其加入到 HTTP kernel 的全局中間件列表中。
當調用中間件上的?`terminate`?方法時,Laravel 將會從服務容器中取出該中間件的新的實例,如果你想要在調用`handle`?和?`terminate`?方法時使用同一個中間件實例,則需要使用容器的?`singleton`?方法將該中間件注冊到容器中。
- 序言
- 發行版本說明
- 升級指南
- 貢獻代碼
- 開始
- 安裝
- 配置
- Laravel Homestead
- 基礎
- HTTP 路由
- HTTP 中間件
- HTTP 控制器
- HTTP 請求
- HTTP 響應
- 視圖
- Blade 模板引擎
- 架構
- 一次請求的生命周期
- 應用目錄結構
- 服務提供者
- 服務容器
- 門面(Facades)
- 數據庫
- 起步
- 查詢構建器
- 遷移
- 填充數據
- Eloquent ORM
- 起步
- 關聯關系
- 集合
- 訪問器&修改器
- 序列化
- 服務
- 用戶認證
- 用戶授權
- Artisan Console
- 訂閱支付實現:Laravel Cashier
- 緩存
- 集合
- 集成前端資源:Laravel Elixir
- 加密
- 錯誤&日志
- 事件
- 文件系統/云存儲
- 哈希
- 輔助函數
- 本地化
- 郵件
- 包開發
- 分頁
- Redis
- 隊列
- Session
- Envoy Task Runner
- 任務調度
- 測試
- 驗證
- 新手入門指南
- 簡單任務管理系統
- 帶用戶功能的任務管理系統