在 Laravel 中,中间件可以根据应用范围分为全局中间件和路由中间件。理解它们的区别与使用场景,能帮助你更精准地控制请求处理流程。 本章将深入讲解这两种中间件的配置方式、应用场景及最佳实践。
根据执行范围,中间件主要分为以下三类:
全局中间件会作用于应用程序的每一个请求,无论路由如何定义。常用于:
在 Laravel 11 中,全局中间件的配置集中在 bootstrap/app.php 文件中:
->withMiddleware(function (Middleware $middleware) {
$middleware->append([
\App\Http\Middleware\LogRequests::class,
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
]);
// 也可以使用 prepend 将中间件添加到列表开头
$middleware->prepend([
\App\Http\Middleware\ForceJsonResponse::class,
]);
})
全局中间件按照定义的顺序依次执行。
app/Http/Kernel.php 的 $middleware 属性中。
路由中间件只对特定路由或路由组生效,常用于:
首先为中间件定义一个别名,同样在 bootstrap/app.php 中:
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'auth' => \App\Http\Middleware\Authenticate::class,
'role' => \App\Http\Middleware\CheckRole::class,
]);
})
然后在路由中使用别名:
Route::get('/dashboard', function () {
// 仅认证用户可访问
})->middleware('auth');
// 带参数的中间件
Route::get('/admin', function () {
// 仅 admin 角色可访问
})->middleware('role:admin');
中间件组是将多个中间件打包成一个集合,可以像单个中间件一样应用到路由上。Laravel 默认提供了 web 和 api 两个组。
自定义中间件组(Laravel 11):
->withMiddleware(function (Middleware $middleware) {
$middleware->group('admin', [
'auth',
'role:admin',
\App\Http\Middleware\LogAdminActions::class,
]);
})
在路由中使用组:
Route::middleware('admin')->group(function () {
Route::get('/users', [UserController::class, 'index']);
Route::get('/settings', [SettingController::class, 'edit']);
});
| 特性 | 全局中间件 | 路由中间件 |
|---|---|---|
| 执行范围 | 所有请求 | 指定路由/路由组 |
| 配置位置 | bootstrap/app.php 中的 append/prepend |
bootstrap/app.php 中的 alias + 路由声明 |
| 典型用途 | 全局性功能(日志、维护模式、CORS) | 特定业务逻辑(认证、授权) |
| 是否可带参数 | 否(但可以在中间件类中读取配置) | 是(通过路由传递) |
| 性能影响 | 影响所有请求,需谨慎添加 | 仅影响部分路由,更可控 |
中间件的执行顺序如下:
append/prepend 中定义的顺序)如果路由使用了中间件组,组内的中间件会按定义顺序依次执行。
可以通过 php artisan route:list 查看每个路由应用的中间件列表,便于调试。
假设我们需要一个博客系统,要求:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class LogRequests
{
public function handle(Request $request, Closure $next)
{
Log::info('请求日志', [
'method' => $request->method(),
'url' => $request->fullUrl(),
'ip' => $request->ip(),
]);
return $next($request);
}
}
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CheckRole
{
public function handle(Request $request, Closure $next, $role)
{
if (! $request->user() || ! $request->user()->hasRole($role)) {
abort(403, '无权限');
}
return $next($request);
}
}
bootstrap/app.php)->withMiddleware(function (Middleware $middleware) {
// 全局中间件
$middleware->append([
\App\Http\Middleware\LogRequests::class,
]);
// 路由中间件别名
$middleware->alias([
'auth' => \App\Http\Middleware\Authenticate::class,
'role' => \App\Http\Middleware\CheckRole::class,
]);
// 后台中间件组
$middleware->group('admin', [
'auth',
'role:admin',
]);
})
Route::middleware('admin')->group(function () {
Route::resource('posts', PostController::class);
Route::get('/dashboard', [DashboardController::class, 'index']);
});
// 公共路由不受后台中间件影响
Route::get('/', [HomeController::class, 'index']);
这样,所有请求都会被记录日志,而只有后台路由会经过认证和角色检查。
bootstrap/app.php 的 withMiddleware 回调中,根据条件动态添加中间件,或者将不需要的中间件移除。但对于已注册的全局中间件,无法针对单个路由禁用。如果需要针对特定路由跳过,应将该中间件改为路由中间件。
$this->middleware() 指定的中间件,本质上属于路由中间件,因为它们只作用于该控制器的方法,且是在路由匹配后执行的。这种方式在 Laravel 11 中依然有效。
php artisan route:list 命令,输出表格中的 Middleware 列会显示该路由经过的所有中间件(包括全局和路由中间件)。
全局中间件和路由中间件各有分工,合理选择可以构建高效且易于维护的应用。全局中间件适合横切关注点,而路由中间件则精准控制特定路由的行为。
在 Laravel 11 中,中间件配置更加集中,使用 bootstrap/app.php 可以灵活管理。掌握这些知识后,你就能游刃有余地处理请求过滤需求。
下一章我们将学习表单请求验证,进一步提升数据处理的优雅性。