本文目录导读:

这是一个非常好的技术问题,在 PHP 项目中,责任链模式(Chain of Responsibility) 和 中间件(Middleware) 本质上都是面向切面编程和请求预处理的实现方式,但它们在设计理念、实现粒度和典型应用场景上有显著区别。
核心概念对比
| 维度 | 责任链模式 | 中间件 |
|---|---|---|
| 本质 | 设计模式(GoF) | 架构风格(受中间件框架启发) |
| 传递机制 | 沿着链条逐个处理,直到某个节点处理完或终止 | 类似“洋葱圈”模型,层层包裹,处理完返回 |
| 控制反转 | 链上节点决定是否继续传递 | 由中间件调用 $next($request) 触发下一个 |
| 典型应用 | 日志、权限校验、数据过滤 | Laravel 路由中间件、PSR-15 中间件 |
| 执行顺序 | 顺序执行,可中断 | 入栈(前置)→ 核心处理 → 出栈(后置) |
责任链模式(Chain of Responsibility)
适用场景
- 多个处理器按顺序尝试处理同一个请求
- 每个处理器可独立决定是否处理或传递给下一个
- 请求的唯一处理者不确定,由运行时决定
经典实现(PHP 示例)
// 抽象处理器
abstract class Handler
{
protected ?Handler $next = null;
public function setNext(Handler $handler): Handler
{
$this->next = $handler;
return $handler; // 返回下一个,便于链式调用
}
abstract public function handle(Request $request): ?Response;
}
// 具体处理器:日志
class LoggingHandler extends Handler
{
public function handle(Request $request): ?Response
{
// 前置处理
echo "[LOG] 记录请求: {$request->uri}\n";
// 传递给下一个(如有)
if ($this->next) {
return $this->next->handle($request);
}
return null;
}
}
// 具体处理器:权限检查
class AuthHandler extends Handler
{
public function handle(Request $request): ?Response
{
if (!$request->hasToken()) {
echo "[AUTH] 未授权,终止链!\n";
return new Response(403, 'Forbidden'); // 阻断链条
}
echo "[AUTH] 用户已验证\n";
if ($this->next) {
return $this->next->handle($request);
}
return null;
}
}
// 使用
$request = new Request('/api/data', ['token' => 'abc']);
$chain = new LoggingHandler();
$chain->setNext(new AuthHandler())
->setNext(new ValidationHandler());
$response = $chain->handle($request);
特点:
- 每个处理器内部手动调用
$this->next->handle() - 可以在任意节点决定返回(中断链条)
中间件(Middleware)
适用场景
- 请求/响应需要经过一系列预处理和后处理(如 CSRF 保护、Session 启动、CORS 头)
- 所有中间件都需要执行,顺序重要
- 需要“双向”处理(进去时做前置,返回时做后置)
洋葱模型(Laravel/PSR-15 风格)
// PSR-15 中间件接口
interface MiddlewareInterface
{
public function process(
Request $request,
RequestHandlerInterface $handler
): Response;
}
// 具体中间件
class CorsMiddleware implements MiddlewareInterface
{
public function process(Request $request, RequestHandlerInterface $handler): Response
{
// 前置处理
$response = $handler->handle($request); // 调用下一个中间件
// 后置处理
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
return $response;
}
}
// 简单实现的核心处理器
class CoreHandler implements RequestHandlerInterface
{
public function handle(Request $request): Response
{
// 真正的业务逻辑
return new Response(200, 'Hello');
}
}
// 中间件调度器
class MiddlewareDispatcher implements RequestHandlerInterface
{
private array $middlewareQueue;
private int $index = 0;
public function __construct(array $middlewareQueue)
{
$this->middlewareQueue = $middlewareQueue;
}
public function handle(Request $request): Response
{
if ($this->index < count($this->middlewareQueue)) {
$middleware = $this->middlewareQueue[$this->index];
$this->index++;
return $middleware->process($request, $this); // 递归调用
}
// 所有中间件处理完毕,执行核心处理
return new Response(200, 'Final Response');
}
}
关键区别总结
| 特性 | 责任链 | 中间件 |
|---|---|---|
| 调用方式 | 手动调用 $next->handle() |
回调/闭包 $next($request) |
| 双向性 | 一般只有前置处理(可做后置但麻烦) | 天然支持前后置处理(洋葱圈) |
| 中断机制 | 任意节点可以返回(不调用 $next) |
通过异常抛出或直接返回 Response 中断 |
| 代码组织 | 链式结构,顺序由 setNext 决定 |
管道式结构,顺序由数组/栈决定 |
| 框架支持 | 较少直接提供,需手动实现 | Laravel、Slim、Symfony 内置 |
实际项目中的选择建议
选用责任链模式(手动实现)
- 当你需要运行时动态组装处理器,且处理器可能选择性跳过
- 典型的如:验证链(用户输入 → 格式校验 → 业务校验 → 数据库校验)
- 或者当每个处理器都**可独立成功”,不需要统一的前后处理
选用中间件(推荐框架内置)
- 在现代化 PHP 框架(Laravel、Symfony、Slim)中,始终优先使用中间件
- 因为框架已经为你实现了:
- 依赖注入
- 路由匹配
- 异常处理包装
- 调度器(Dispatcher)
混合使用
有时两者可以结合:
Request → AuthMiddleware → LogMiddleware → ChainHandler(责任链) → Controller
性能与维护注意事项
- 中间件过多会线性增加请求处理时间,建议控制在 10 个以内
- 责任链中不要做同步 IO 操作,每个节点应快速返回
- 注意执行顺序:
- 责任链中:
handle()的调用顺序决定 - 中间件中:通常先注册的后执行(Laravel 中是先注册的先出栈)
- 责任链中:
| 项目规模 | 推荐方式 |
|---|---|
| 小项目 / 自建框架 | 责任链模式(更灵活) |
| 使用 Laravel/Symfony | 中间件(框架原生支持) |
| 需要双向处理(前后置) | 中间件 |
| 需要动态跳过处理器 | 责任链 |
| 两者二选一 | 选中间件,更符合现代 PHP 生态 |