什么是中间件
中间件是一种检查、过滤到达应用的 HTTP 请求的机制。
你可以在请求到达控制器前后插入处理逻辑。
比如 Laravel 内置了认证中间件。
当用户未认证时,会重定向到登录页面;若已认证,则允许请求继续进入应用内部。
除认证外,还可以为日志记录、CSRF 保护、限流等各种目的编写中间件。关于 Laravel 13 CSRF 保护的详细内容,请参阅 CSRF 保护。
用户自定义的中间件通常放在 app/Http/Middleware 目录下。
内置中间件
Laravel 默认准备了 web 与 api 两个中间件组。
routes/web.php 会自动应用 web 组,routes/api.php 会自动应用 api 组。
web 中间件组 |
|---|
EncryptCookies |
AddQueuedCookiesToResponse |
StartSession |
ShareErrorsFromSession |
PreventRequestForgery(CSRF 保护) |
SubstituteBindings |
对常用中间件,还设置了别名,可以用简短的名字引用。
| 别名 | 中间件 |
|---|
auth | Illuminate\Auth\Middleware\Authenticate |
guest | Illuminate\Auth\Middleware\RedirectIfAuthenticated |
verified | Illuminate\Auth\Middleware\EnsureEmailIsVerified |
throttle | Illuminate\Routing\Middleware\ThrottleRequests |
创建中间件
使用 make:middleware Artisan 命令创建新的中间件。
php artisan make:middleware EnsureTokenIsValid
会生成 app/Http/Middleware/EnsureTokenIsValid.php。
在 handle 方法中编写处理请求的逻辑。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureTokenIsValid
{
/**
* 处理传入的请求
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('/home');
}
return $next($request);
}
}
调用 $next($request) 会将请求交给应用的下一环处理。
若不满足条件,可以返回重定向或响应,从而阻止请求继续。
请求前后的处理
中间件可以在请求之前或之后执行逻辑。
// 在请求前处理
public function handle(Request $request, Closure $next): Response
{
// 前置处理写在这里
return $next($request);
}
// 在响应后处理
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// 后置处理写在这里
return $response;
}
注册中间件
全局中间件
若希望所有请求都执行该中间件,请在 bootstrap/app.php 的 withMiddleware 中添加到全局栈。
// bootstrap/app.php
use App\Http\Middleware\EnsureTokenIsValid;
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware): void {
$middleware->append(EnsureTokenIsValid::class);
});
append 追加到栈末尾。要放到最前面请用 prepend。
对路由应用中间件
若只对特定路由应用中间件,可在路由定义上调用 middleware。
use App\Http\Middleware\EnsureTokenIsValid;
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);
要应用多个中间件,用数组传入。
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);
若要在特定路由上排除某个中间件,可使用 withoutMiddleware。
use App\Http\Middleware\EnsureTokenIsValid;
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// 应用了该中间件
});
Route::get('/profile', function () {
// 该路由排除中间件
})->withoutMiddleware([EnsureTokenIsValid::class]);
});
中间件别名
可以为较长的类名设置短别名。在 bootstrap/app.php 中配置。
// bootstrap/app.php
use App\Http\Middleware\EnsureUserIsSubscribed;
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class,
]);
});
使用别名应用到路由:
Route::get('/profile', function () {
// ...
})->middleware('subscribed');
中间件组
将多个中间件归为一个键,便于统一应用到路由。
在 bootstrap/app.php 中使用 appendToGroup 或 prependToGroup。
// bootstrap/app.php
use App\Http\Middleware\First;
use App\Http\Middleware\Second;
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware): void {
$middleware->appendToGroup('group-name', [
First::class,
Second::class,
]);
});
分组可像普通中间件一样应用到路由。
Route::get('/', function () {
// ...
})->middleware('group-name');
Route::middleware(['group-name'])->group(function () {
// ...
});
要向已有的 web 或 api 分组添加中间件,可使用专用方法。
// bootstrap/app.php
use App\Http\Middleware\EnsureUserIsSubscribed;
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
EnsureUserIsSubscribed::class,
]);
});
中间件参数
中间件可以接收额外参数。
在 handle 方法的 $next 参数之后添加参数即可。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user()->hasRole($role)) {
return redirect('/home');
}
return $next($request);
}
}
在路由定义中,中间件名和参数用 : 分隔。
use App\Http\Middleware\EnsureUserHasRole;
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor');
多个参数用逗号分隔。
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');
实用示例:认证检查中间件
一个简单的示例:把未登录用户重定向到登录页。
php artisan make:middleware RedirectIfNotAuthenticated
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfNotAuthenticated
{
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()) {
return redirect('/login');
}
return $next($request);
}
}
应用到路由:
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(RedirectIfNotAuthenticated::class);
Laravel 已内置用于认证的 auth 中间件。自行实现前请先确认现有中间件能否满足需求。
下一步