Laravel 中间件的中间件依赖注入与中间件执行的条件分支策略

🎤 Laravel 中间件讲座:依赖注入与条件分支策略的奇妙之旅

大家好!欢迎来到今天的 Laravel 技术讲座 🎉。今天我们要聊的是中间件(Middleware)这个 Laravel 的核心概念,特别是它的 依赖注入条件分支策略。如果你觉得这些听起来很复杂,别担心!我会用轻松诙谐的语言和代码示例带你一步步理解。


🛠️ 什么是中间件?

在 Laravel 中,中间件就像一个“守门人”,它可以在 HTTP 请求到达控制器之前或之后执行一些操作。比如:

  • 验证用户是否登录
  • 检查用户是否有权限访问某个资源
  • 记录请求日志
  • 压缩响应数据

简单来说,中间件就是一个函数,接收输入并返回输出,同时可以对输入或输出进行处理。


🔍 中间件依赖注入

1. 什么是依赖注入?

依赖注入(Dependency Injection, DI)是现代 PHP 开发中非常重要的设计模式。它的核心思想是:不要自己创建对象,而是让外部将对象传递进来

在 Laravel 中,中间件可以通过构造函数或方法参数实现依赖注入。下面我们通过代码来感受一下。

2. 构造函数中的依赖注入

假设我们有一个 AuthMiddleware,需要使用 Auth 服务来验证用户是否登录。我们可以这样写:

namespace AppHttpMiddleware;

use Closure;
use IlluminateSupportFacadesAuth;

class AuthMiddleware
{
    protected $auth;

    public function __construct(Auth $auth)
    {
        $this->auth = $auth; // 通过构造函数注入 Auth 服务
    }

    public function handle($request, Closure $next)
    {
        if (!$this->auth->check()) {
            return redirect('login'); // 如果未登录,重定向到登录页面
        }

        return $next($request); // 继续处理请求
    }
}

💡 注意:Laravel 的服务容器会自动解析 Auth 对象,并将其传递给中间件的构造函数。这就是依赖注入的魅力!

3. 方法参数中的依赖注入

有时候,你可能不需要在整个中间件生命周期内都使用某个依赖,而只是在 handle 方法中用到一次。这时,你可以直接在方法参数中进行依赖注入:

public function handle($request, Closure $next, IlluminateContractsAuthGuard $auth)
{
    if (!$auth->check()) {
        return redirect('login');
    }

    return $next($request);
}

虽然这种方式也可以工作,但通常推荐在构造函数中注入依赖,因为它更清晰、更符合 SOLID 原则。


🌳 条件分支策略:让中间件更有智慧

有时候,我们希望中间件只在特定条件下执行。例如,只有当用户来自移动端时才应用某种逻辑。这种需求可以通过条件分支策略来实现。

1. 使用 Closure 参数动态传递条件

Laravel 允许我们在注册中间件时动态传递参数。例如:

Route::middleware(['auth:admin'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
});

在这个例子中,auth:admin 表示我们传递了一个参数 adminAuthMiddleware。那么如何在中间件中接收这个参数呢?

public function handle($request, Closure $next, $guard = null)
{
    if ($guard === 'admin' && !$this->auth->guard($guard)->check()) {
        return redirect('login');
    }

    return $next($request);
}

在这里,$guard 是从路由定义中传递过来的参数。如果 $guard 等于 'admin',我们就使用 admin 守卫来验证用户。

2. 根据请求条件动态执行

除了通过参数控制中间件行为,我们还可以根据请求的上下文动态决定是否执行某些逻辑。例如,检查用户是否来自移动端:

public function handle($request, Closure $next)
{
    if ($request->header('User-Agent') && str_contains($request->header('User-Agent'), 'Mobile')) {
        // 如果是移动端请求,执行特定逻辑
        return response('This is a mobile request.');
    }

    return $next($request);
}

📊 总结对比表

为了让大家更清楚地理解中间件依赖注入和条件分支策略的区别,我做了一个简单的对比表:

特性 依赖注入 条件分支策略
作用 注入外部服务或对象 根据条件动态调整中间件的行为
实现方式 构造函数或方法参数 路由参数或请求上下文
适用场景 需要长期使用的外部服务 需要根据条件动态调整逻辑
代码示例 public function __construct(Auth $auth) if ($request->header('User-Agent')) {}

🚀 实战演练

为了让内容更加生动,我们来做一个小练习。假设我们需要实现一个中间件,用于限制 API 请求的频率。要求如下:

  1. 如果用户没有登录,则不限制。
  2. 如果用户已登录,则每分钟最多允许 60 次请求。

下面是实现代码:

namespace AppHttpMiddleware;

use Closure;
use IlluminateSupportFacadesAuth;
use IlluminateSupportFacadesCache;

class ApiThrottleMiddleware
{
    public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        if (!Auth::check()) {
            return $next($request); // 如果未登录,不限制
        }

        $key = 'api_throttle_' . Auth::id();
        $attempts = Cache::increment($key);

        if ($attempts === 1) {
            Cache::put($key, 0, now()->addMinutes($decayMinutes));
        }

        if ($attempts > $maxAttempts) {
            return response()->json(['error' => 'Too many requests'], 429);
        }

        return $next($request);
    }
}

💡 提示:这里的 Cache 服务也是通过依赖注入的方式引入的,但为了简化代码,我们直接使用了 Facade。


🎉 结语

好了,今天的讲座就到这里啦!我们学习了 Laravel 中间件的依赖注入和条件分支策略。通过这些技术,你可以让中间件变得更灵活、更强大。

如果你觉得这篇文章有用,请给我点个赞 👏,或者在评论区告诉我你的想法 😊。下次见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注