Laravel 的 HTTP 中间件管道(Middleware Pipeline)确实是责任链模式(Chain of Responsibility)的精妙实现,但它与经典责任链略有不同:它不仅支持“由某一个处理器处理请求”,更支持“所有处理器依次处理请求和响应”,形成双向的“洋葱模型”(Onion Model)


一、责任链模式的核心思想(GoF 定义)

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止

  • Handler(处理器):定义处理接口;
  • ConcreteHandler(具体处理器):处理请求或传递给下一个;
  • 关键:每个处理器决定是否处理请求,以及是否传递。

在经典责任链中:

  • 通常只有一个处理器处理请求(如审批流:经理 → 总监 → CEO);
  • 处理即终止,不再传递。

二、Laravel 中间件管道:责任链的“增强版”

Laravel 的中间件管道 扩展了责任链模式,使其支持:

  1. 所有中间件都处理请求(进)
  2. 所有中间件都处理响应(出)
  3. 可提前终止(如认证失败)
1. 中间件接口

每个中间件实现 handle() 方法:

class Authenticate
{
    public function handle($request, Closure $next)
    {
        if (! auth()->check()) {
            return redirect('/login'); // ← 提前终止
        }

        // 传递请求到下一层
        $response = $next($request);

        // 可选:处理响应(出)
        $response->header('X-Auth', 'ok');

        return $response;
    }
}
2. $next($request) 的作用
  • $next 是“下一个中间件”的引用(本质是一个闭包);
  • $next($request) 触发链式调用,将请求传递给下一个中间件;
  • 返回值是最终响应(Response),可被当前中间件修改。

🔑 $next($request) 是责任链的“传递机制”


三、管道(Pipeline)如何组装责任链?

Laravel 使用 Illuminate\Pipeline\Pipeline 类动态构建中间件链。

1. 中间件数组
$middleware = [
    TrustProxies::class,
    ValidatePostSize::class,
    TrimStrings::class,
    ConvertEmptyStringsToNull::class,
    Authenticate::class,
];
2. 管道组装(洋葱模型)

Pipeline 通过 array_reduce 从右到左嵌套中间件:

// 伪代码:组装后的调用链
$first = function ($request) use ($destination) {
    return $destination($request);
};

$second = function ($request) use ($first, $Authenticate) {
    return $Authenticate->handle($request, $first);
};

$third = function ($request) use ($second, $TrimStrings) {
    return $TrimStrings->handle($request, $second);
};

// ... 最终得到最外层中间件
$response = $outermostMiddleware($request);

🧅 执行流程(洋葱模型)

TrustProxies → ValidatePostSize → ... → Controller
Controller → ... → ValidatePostSize → TrustProxies
3. 提前终止

如果某个中间件不调用 $next($request),而是直接返回响应:

if (! $request->isValid()) {
    return response('Invalid', 400); // ← 链终止
}
  • 后续中间件和控制器不会执行
  • 已执行的中间件仍可处理响应(但通常不会,因为 $next 未调用)。

四、与经典责任链模式的区别

特性 经典责任链 Laravel 中间件管道
处理目标 一个处理器处理请求 所有处理器处理请求和响应
传递方向 单向(请求 → 处理器) 双向(请求进 + 响应出)
终止条件 任一处理器处理即终止 可提前终止,也可全链执行
典型场景 审批流、日志级别过滤 HTTP 请求处理(认证、日志、CORS)

Laravel 的管道是“增强的责任链”,更适用于 Web 请求的横切关注点处理。


五、$next($request) 的深层机制

1. $next 是什么?
  • $next 是一个 闭包(Closure),封装了“剩余中间件链 + 控制器”;
  • Pipeline 动态生成。
2. 为什么需要 $next
  • 解耦中间件:每个中间件无需知道下一个是谁;
  • 控制执行流:决定是否继续、何时继续;
  • 支持响应修改:在 $next 返回后操作响应。
3. 错误示例:忘记调用 $next
public function handle($request, Closure $next)
{
    Log::info('Request started');
    // 忘记 return $next($request);
    // → 请求被静默丢弃,无响应!
}

⚠️ 必须调用 $next($ request),除非你打算提前终止


六、与你工程理念的深度对齐

你的原则 在中间件管道中的体现
关注点分离 认证、日志、HTTPS 强制等各司其职
可组合性 通过 $middleware 数组灵活组合中间件
可测试性 每个中间件可独立测试(传入 Mock Request/Next)
避免硬编码 if ($needsAuth) authenticate(),行为由管道定义
SOLID 遵循 符合单一职责(每个中间件只做一件事)

七、高级用法:动态中间件与条件链

1. 动态添加中间件
// 在控制器中
$this->middleware(EnsureUserIsAdmin::class)->only('destroy');
2. 条件中间件
// Kernel.php
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        // ...
    ],
    'api' => [
        'throttle:60,1',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];
3. 自定义管道
// 非 HTTP 场景(如任务处理)
$tasks = [LogTask::class, ValidateTask::class];
$result = (new Pipeline)
    ->send($data)
    ->through($tasks)
    ->then(fn($data) => $data->process());

Pipeline 不仅用于 HTTP,还可用于任何链式处理场景


结语

Laravel 的中间件管道 是责任链模式在 Web 开发中的创造性演进。它通过:

$next($request) 传递机制 + 双向洋葱模型 + 提前终止能力

实现了:

  • 横切关注点的完美解耦
  • 请求/响应处理的灵活组合
  • 高性能的链式执行

正如你所理解的:好的架构让复杂性可控,而非消失
中间件管道将认证、日志、安全等复杂逻辑封装成可组合的链条,让控制器保持纯净——这正是责任链模式在现代 Web 框架中的最高价值。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐