Laravel 11 全局中间件与路由中间件

在 Laravel 中,中间件可以根据应用范围分为全局中间件和路由中间件。理解它们的区别与使用场景,能帮助你更精准地控制请求处理流程。 本章将深入讲解这两种中间件的配置方式、应用场景及最佳实践。

🎯 中间件的分类

根据执行范围,中间件主要分为以下三类:

  • 全局中间件:对每个 HTTP 请求都会执行。
  • 路由中间件:仅对指定路由或路由组执行。
  • 中间件组:将多个中间件打包,方便批量应用。

🌍 全局中间件

全局中间件会作用于应用程序的每一个请求,无论路由如何定义。常用于:

  • 请求日志记录
  • 维护模式检查
  • 跨域请求处理(CORS)
  • 请求数据预处理(如 TrimStrings)
配置全局中间件(Laravel 11)

在 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,
    ]);
})

全局中间件按照定义的顺序依次执行。

💡 提示: 在旧版 Laravel(≤10)中,全局中间件定义在 app/Http/Kernel.php$middleware 属性中。

🔗 路由中间件

路由中间件只对特定路由或路由组生效,常用于:

  • 身份验证(Auth)
  • 权限检查(如角色、能力)
  • API 速率限制
  • 特定场景的数据过滤
注册路由中间件(Laravel 11)

首先为中间件定义一个别名,同样在 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 默认提供了 webapi 两个组。

自定义中间件组(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']);
});

⚖️ 全局 vs 路由中间件对比

实例
特性 全局中间件 路由中间件
执行范围 所有请求 指定路由/路由组
配置位置 bootstrap/app.php 中的 append/prepend bootstrap/app.php 中的 alias + 路由声明
典型用途 全局性功能(日志、维护模式、CORS) 特定业务逻辑(认证、授权)
是否可带参数 否(但可以在中间件类中读取配置) 是(通过路由传递)
性能影响 影响所有请求,需谨慎添加 仅影响部分路由,更可控

🔄 执行顺序

中间件的执行顺序如下:

  1. 全局中间件(按照在 append/prepend 中定义的顺序)
  2. 路由中间件(按照在路由中定义的顺序)

如果路由使用了中间件组,组内的中间件会按定义顺序依次执行。

可以通过 php artisan route:list 查看每个路由应用的中间件列表,便于调试。

🎨 实战示例:构建一个完整的中间件体系

假设我们需要一个博客系统,要求:

  • 所有请求记录访问日志(全局中间件)
  • 后台管理路由需要认证和角色检查(路由中间件)
1. 创建全局日志中间件
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);
    }
}
2. 创建角色检查中间件
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);
    }
}
3. 配置中间件(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',
    ]);
})
4. 在路由中使用
Route::middleware('admin')->group(function () {
    Route::resource('posts', PostController::class);
    Route::get('/dashboard', [DashboardController::class, 'index']);
});

// 公共路由不受后台中间件影响
Route::get('/', [HomeController::class, 'index']);

这样,所有请求都会被记录日志,而只有后台路由会经过认证和角色检查。

✅ 最佳实践

  • 谨慎使用全局中间件:全局中间件会影响所有请求,应避免添加消耗大的操作,以免拖慢整体性能。
  • 路由中间件参数化:充分利用中间件参数,实现可复用的权限检查逻辑。
  • 合理分组:对于功能相似的路由,使用中间件组可以减少重复代码。
  • 保持顺序清晰:理解中间件执行顺序,避免因顺序不当导致的逻辑错误(如认证中间件应优先于业务中间件)。
  • 测试中间件:为自定义中间件编写单元测试,确保其行为符合预期。

❓ 常见问题

在 Laravel 11 中,可以在 bootstrap/app.phpwithMiddleware 回调中,根据条件动态添加中间件,或者将不需要的中间件移除。但对于已注册的全局中间件,无法针对单个路由禁用。如果需要针对特定路由跳过,应将该中间件改为路由中间件。

在控制器构造函数中使用 $this->middleware() 指定的中间件,本质上属于路由中间件,因为它们只作用于该控制器的方法,且是在路由匹配后执行的。这种方式在 Laravel 11 中依然有效。

使用 php artisan route:list 命令,输出表格中的 Middleware 列会显示该路由经过的所有中间件(包括全局和路由中间件)。

📝 小结

全局中间件和路由中间件各有分工,合理选择可以构建高效且易于维护的应用。全局中间件适合横切关注点,而路由中间件则精准控制特定路由的行为。 在 Laravel 11 中,中间件配置更加集中,使用 bootstrap/app.php 可以灵活管理。掌握这些知识后,你就能游刃有余地处理请求过滤需求。 下一章我们将学习表单请求验证,进一步提升数据处理的优雅性。