* * * * *
[TOC]
## 簡介
Laravel 中間件提供了一種方便的機制來過濾進入應用的 HTTP 請求,例如,Laravel 包含驗證用戶身份權限的中間件。如果用戶沒有通過身份驗證,中間件會重定向到登錄頁,引導用戶登錄。反之,中間件將允許該請求繼續傳遞到應用程序。
當然,除了身份驗證以外,中間件還可以被用來執行各式各樣的任務,如:CORS 中間件負責為所有即將離開應用的響應添加適當的頭信息;日志中間件可以記錄所有傳入應用的請求。
Laravel 已經內置了一些中間件,包括身份驗證、CSRF 保護等。所有的中間件都放在?`app/Http/Middleware`?目錄內。
## 創建中間件
使用?`make:middleware`?這個 Artisan 命令創建新的中間件:
~~~
php artisan make:middleware CheckAge
~~~
該命令將會在?`app/Http/Middleware`?目錄內新建一個?`CheckAge`?類。在這個中間件內,我們僅允許請求的?`age`參數大于 200 時訪問該路由,否則,會將用戶請求重定向到?`home`?URI 。
~~~
<?php
namespace App\Http\Middleware;
use Closure;
class CheckAge
{
/**
* 處理傳入的請求
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->age <= 200) {
return redirect('home');
}
return $next($request);
}
}
~~~
如你所見,若請求參數?`age`?小于等于?`200`,中間件將返回給客戶端 HTTP 重定向,反之應用程序才會繼續處理該請求。若將請求繼續傳遞到應用程序(即允許通過中間件驗證),只需將?`$request`?作為參數調用?`$next`?回調函數。
最好將中間件想象為一系列的「層」,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`?文件內為該中間件指定一個?`鍵值`。默認情況下?`Kernel`?類的?`$routeMiddleware`?屬性已經包含了 Laravel 內置的中間件條目。加入自定義的中間件,只需把它附加到此列表并指定你定義的`鍵值`即可。例如:
~~~
// App\Http\Kernel 類內
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,
];
~~~
一旦在 HTTP kernel 文件內定義了中間件,即可使用?`middleware`?方法將中間件分配給路由:
~~~
Route::get('admin/profile', function () {
//
})->middleware('auth');
~~~
為路由指定多個中間件:
~~~
Route::get('/', function () {
//
})->middleware('first', 'second');
~~~
也可使用完整類名指定中間件:
~~~
use App\Http\Middleware\CheckAge;
Route::get('admin/profile', function () {
//
})->middleware(CheckAge::class);
~~~
### 中間件組
有時您可能想要將多個中間件分組到同一個`鍵值`下,從而使它們更方便地分配給路由,你可以使用 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,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'auth:api',
],
];
~~~
中間件組可以像單個中間件一樣使用相同的語法指定給路由和控制器。重申,路由組僅僅是為了使一次將多個中間件指定給某個路由的實現變得更加方便。
~~~
Route::get('/', function () {
//
})->middleware('web');
Route::group(['middleware' => ['web']], function () {
//
});
~~~
> {tip} 開箱即用的?`web`?中間件組被自動應用于?`RouteServiceProvider`?中定義的?`routes/web.php`?路由組。
## 中間件參數
中間件也可以接受其他附加的參數。例如,如果應用需要在運行特定操作前驗證該用戶具備該操作的權限的「角色」,你可以新建一個?`CheckRole`?中間件,該中間件接收「角色」名字作為附加參數。
附加的中間件參數將在?`$next`?參數之后被傳入:
~~~
<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
/**
* 處理傳入的請求
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $role
* @return mixed
*/
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');
~~~
## Terminable 中間件
有些時候中間件需要在 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)
{
// Store the session data...
}
}
~~~
`terminate`?方法必需接收請求及響應兩個參數。一旦定義了 terminable 中間件,你便需要將它增加到 HTTP kernel 文件的全局中間件清單列表中。
中間件的?`terminate`?調用時,Laravel 會從?[服務容器](服務容器.md)?中解析一個全新的中間件實例。如果你想在?`handle`?和?`terminate`?被調用時使用同一個中間件實例,可使用容器的?`singleton`?方法向容器注冊中間件。
- 前言
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 請求周期
- 開發環境部署
- Homestead
- Valet
- 核心概念
- 服務容器
- 服務提供者
- Facades
- Contracts
- HTTP層
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- Session
- 表單驗證
- 前端
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- Passport OAuth 認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 錯誤與日志
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Scout 全文搜索
- Socialite 社會化登錄