中间件
中间件
自定义中间件通常放在 app/Http/Middleware 目录。
定义中间件
使用 make:middleware 命令可以创建一个中间件:
php artisan make:middleware Auth可以在这个中间件中添加限定,请求 Headers 中必须包含指定的 secret 才能继续执行,否则抛出 401:
class Auth
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$secret = $request->header('x-secret');
if ($secret !== 'PPG test secret') {
return response()->json([
'message' => 'Unauthorized',
], 401);
}
return $next($request);
}
}当中间件需要继续 执行时,需要调用 $next 参数并传入 $request 参数。
通过 $next 和中间件的逻辑顺序的调整,可以实现前置中间件和后置中间件。
提示
所有的中间件都会经过容器解析,这意味着可以在构造函数里注入依赖。
注册中间件
全局中间件
全局中间件会在每个 HTTP 请求中执行,可以在 bootstrap/app.php 中使用 withMiddleware 方法注册:
->withMiddleware(function (Middleware $middleware) {
$middleware->append(Auth::class);
})append 方法会将指定的中间件添加到中间件列表的最后,如果希望从列表头部添加,可以使用 prepend 方法:
->withMiddleware(function (Middleware $middleware) {
$middleware->prepend(Auth::class);
})路由级中间件
如果想给单个路由添加中间件,可以使用 middleware 方法:
Route::controller(DemoController::class)->group(function () {
Route::get('/members/{id}', 'getMember')->name('get');
Route::post('/members', 'createMember')->name('create')->middleware([Auth::class]);
});也可以给路由组添加中间件:
Route::controller(DemoController::class)->middleware([Auth::class])->group(function () {
Route::get('/members/{id}', 'getMember')->name('get');
Route::post('/members', 'createMember')->name('create');
});提示
给路由组添加中间件时,middleware 方法要在 group 方法之前调用。
给一组路由添加中间件时,如果组中的某个路由不需要中间件,那么可以使用 withoutMiddleware 方法:
Route::controller(DemoController::class)->middleware([Auth::class])->group(function () {
Route::get('/members/{id}', 'getMember')->name('get')->withoutMiddleware([Auth::class]);
Route::post('/members', 'createMember')->name('create');
});重要
移除中间件不适用于全局中间件。
中间件组
将中间件分组后可以更简单的分配给路由,可以在 bootstrap/app.php 中使用 appendToGroup 方法:
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('test', [
First::class,
Second::class,
]);
})Route::controller(DemoController::class)->middleware('test')->group(function () {
Route::get('/members/{id}', 'getMember')->name('get');
Route::post('/members', 'createMember')->name('create');
});->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'a' => Auth::class,
'f' => First::class,
's' => Second::class,
]);
})Route::controller(DemoController::class)->middleware(['a', 'f', 's'])->group(function () {
Route::get('/members/{id}', 'getMember')->name('get');
Route::post('/members', 'createMember')->name('create');
});中间件排序
如果需要中间件按顺序执行,那么可以在 bootstrap/app.php 中使用 priority 方法:
->withMiddleware(function (Middleware $middleware) {
$middleware->priority([
First::class,
Second::class,
Auth::class,
]);
})中间件参数
中间件还可以接收参数,参数会在 handle 方法中的 $next 参数后作为参数传入:
如果有多个参数,可以使用 : 分隔:
Route::controller(DemoController::class)->middleware(['a', 'f:demo,test', 's'])->group(function () {
Route::get('/members/{id}', 'getMember')->name('get');
Route::post('/members', 'createMember')->name('create');
});终止后执行中间件
如果需要在响应发出后继续执行一些逻辑,可以在中间件中定义 terminate 方法,此方法将在使用了 FastCGI 的情况下,在响应发送后自动被调用。
<?php
namespace Illuminate\Session\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class TerminatingMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
/**
* Handle tasks after the response has been sent to the browser.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}terminate 方法同时接收请求和响应,终止后执行中间件应该添加到全局中间件中。
在中间件上调用 terminate 方法时,Laravel 将会解析一个新的中间件实例,如果要在调用 handle 和 terminate 方法中使用相同的实例,应该在 AppServiceProvider 中将这个中间件注册为单例。