Laravel 11 中间件创建与注册

中间件是 Laravel 中实现请求过滤的核心机制,它可以在请求到达控制器之前或响应返回客户端之后执行特定逻辑。 本章将系统讲解如何创建自定义中间件,并将其注册到应用中。

🎯 什么是中间件?

中间件为应用程序提供了一个方便的机制,用于过滤进入应用的 HTTP 请求。例如,Laravel 内置了身份验证中间件,用于验证用户是否已登录。 如果用户未登录,中间件会将用户重定向到登录页面;如果用户已登录,则允许请求继续执行。

中间件可以用于:

  • 身份验证
  • 权限检查
  • 请求日志记录
  • 维护模式
  • CORS 处理
  • 请求内容转换

📦 创建中间件

使用 Artisan 命令 make:middleware 可以快速生成中间件:

php artisan make:middleware CheckAge

该命令会在 app/Http/Middleware 目录下创建 CheckAge.php 文件,内容如下:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckAge
{
    /**
     * 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);
    }
}
💡 提示: Laravel 11 中的中间件默认使用 Closure 类型提示,并返回 Response 对象。你可以根据需要修改 handle 方法的逻辑。

✍️ 编写中间件逻辑

中间件的核心是 handle 方法。它接收当前请求 $request 和一个闭包 $next,闭包代表将请求传递到下一个中间件或控制器。

以下是一个检查年龄的中间件示例:

public function handle(Request $request, Closure $next): Response
{
    if ($request->age < 18) {
        return redirect('home');
    }

    return $next($request);
}

如果年龄小于 18,中间件返回重定向响应,阻止请求继续;否则调用 $next($request) 将请求传递给下一个中间件。

中间件参数

中间件还可以接收额外参数,在路由中定义时传递:

public function handle(Request $request, Closure $next, string $role): Response
{
    if (! $request->user() || ! $request->user()->hasRole($role)) {
        abort(403);
    }
    return $next($request);
}

注册时需要指定参数值,例如:->middleware('role:admin')

📌 注册中间件

创建中间件后,需要将其注册到应用中才能生效。Laravel 提供了三种注册方式:全局中间件、路由中间件和中间件组。

1. 全局中间件

全局中间件会在每个 HTTP 请求中自动执行。注册位置在 app/Http/Kernel.php 文件的 $middleware 属性中:

protected $middleware = [
    // \App\Http\Middleware\TrustHosts::class,
    \App\Http\Middleware\TrustProxies::class,
    \Illuminate\Http\Middleware\HandleCors::class,
    \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    // 添加自定义全局中间件
    \App\Http\Middleware\CheckAge::class,
];
⚠️ 注意: Laravel 11 中的 Kernel 类可能简化了中间件结构,但全局中间件的注册方式基本一致。如果 Kernel.php 不存在,可检查 bootstrap/app.php 中的中间件配置。
2. 路由中间件

路由中间件只针对特定路由执行,需要在 Kernel.php$routeMiddleware 属性中为中间件分配一个键名:

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    'age' => \App\Http\Middleware\CheckAge::class,  // 自定义路由中间件
];

然后在路由中使用 middleware 方法:

Route::get('/dashboard', function () {
    // 只有年龄符合要求的用户才能访问
})->middleware('age');

也可以传递参数:->middleware('age:18'),中间件会接收 18 作为参数。

3. 中间件组

中间件组允许将多个中间件组合在一起,方便批量应用。Laravel 内置了 webapi 两个中间件组。可以在 Kernel.php$middlewareGroups 中定义自定义组:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // ...
    ],

    'api' => [
        \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    // 自定义中间件组
    'admin' => [
        'auth',
        'age:18',
        \App\Http\Middleware\AdminRole::class,
    ],
];

在路由中使用组:Route::middleware('admin')->group(function () { ... });

💡 Laravel 11 变化: 从 Laravel 11 开始,默认的 Kernel 类可能不再存在,中间件配置迁移到了 bootstrap/app.php 中。如果使用新版,请参考以下配置方式。

🆕 Laravel 11 中的中间件配置

在 Laravel 11 中,中间件的注册方式有所简化,主要配置集中在 bootstrap/app.php 文件中:

->withMiddleware(function (Middleware $middleware) {
    // 全局中间件
    $middleware->append([
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ]);

    // 路由中间件别名
    $middleware->alias([
        'auth' => \App\Http\Middleware\Authenticate::class,
        'age' => \App\Http\Middleware\CheckAge::class,
    ]);

    // 中间件组
    $middleware->group('web', [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // ...
    ]);
})

使用时与旧版本相同,路由中通过别名引用。

🔚 终止中间件

如果中间件需要在响应发送到浏览器后执行一些任务(如记录日志),可以实现 terminate 方法:

class LogRequestMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        return $next($request);
    }

    public function terminate(Request $request, Response $response)
    {
        // 响应发送后执行,如记录日志
        Log::info('请求处理完成', [
            'url' => $request->url(),
            'status' => $response->getStatusCode(),
        ]);
    }
}

只有全局中间件和路由中间件(且通过 Kernel 注册)支持 terminate 方法。在 Laravel 11 中,终止中间件的注册方式可能略有不同,但原理一致。

🎨 实战示例:检查用户角色的中间件

以下是一个完整的中间件创建、注册和使用示例:

1. 创建中间件:
php artisan make:middleware CheckRole

2. 编写中间件逻辑:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckRole
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (! $request->user() || ! $request->user()->hasRole($role)) {
            abort(403, '无权限访问');
        }

        return $next($request);
    }
}

3. 注册中间件(Laravel 11 方式):
bootstrap/app.php 中:

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \App\Http\Middleware\CheckRole::class,
    ]);
})

4. 在路由中使用:

Route::get('/admin/dashboard', function () {
    return '管理员仪表盘';
})->middleware('role:admin');

❓ 常见问题

全局中间件按注册顺序执行,路由中间件按路由中定义的顺序执行,两者组合时,全局中间件先于路由中间件执行。

可以在控制器的构造函数中使用 $this->middleware() 方法,例如:$this->middleware('auth');,支持 onlyexcept 参数。

Laravel 11 将中间件配置迁移到了 bootstrap/app.php 中,使用 withMiddleware 方法进行配置。请参照本章的新版说明。

📝 小结

中间件是 Laravel 中强大而灵活的请求过滤机制。通过 Artisan 命令可以快速创建中间件,并在 bootstrap/app.php(Laravel 11)或 Kernel.php(旧版)中注册。 掌握中间件的创建与注册,能够让你轻松实现身份验证、权限控制、日志记录等横切关注点,保持代码整洁。 下一章我们将学习会话管理,进一步掌握用户状态维护的技巧。