中间件
中间件(Middleware)采用洋葱模型,在路由处理器前后插入横切逻辑(鉴权、限流、日志、跨域等)。Viswoole 的中间件分为三个层级:全局中间件、服务级中间件与路由级中间件,请求按「全局 → 服务级 → 路由级」的顺序依次穿过。
注册中间件
通过 Viswoole\Core\Facade\Middleware 门面的 register() 方法注册:
Middleware::register(callable|string|array $handler, ?string $server = null): void| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
handler | callable|string|array | 无 | 中间件处理器,支持下文三种形式 |
server | string|null | null | 服务名称;null 时注册为全局中间件,否则注册为对应服务的服务级中间件 |
use Viswoole\Core\Facade\Middleware;
// 全局中间件:作用于所有服务
Middleware::register(LogMiddleware::class);
// 服务级中间件:仅作用于名为 http 的服务
Middleware::register(AuthMiddleware::class, 'http');INFO
路由在服务启动时装配,请求分发阶段才会执行中间件,因此注册代码需早于首次请求处理——推荐放在服务提供者的 boot() 或 config/listens.php 注册的事件监听中。中间件注册属于进程级配置,修改后需重启服务。
中间件的三种形式
1. 闭包
闭包中间件适合轻量逻辑,参数由容器按类型注入,其中名为 $handler 的参数接收「下一个中间件 + 处理器」组成的闭包,必须调用 $handler() 才能继续执行后续链路:
use Closure;
use Viswoole\Core\Facade\Middleware;
use Viswoole\HttpServer\Contract\{RequestInterface, ResponseInterface};
Middleware::register(function (RequestInterface $request, ResponseInterface $response, Closure $handler) {
// 前置逻辑
$response->setHeader('X-Request-Time', (string) time());
$result = $handler(); // 不调用则请求在此终止
// 后置逻辑
return $result;
}, 'http');2. 类中间件(推荐)
类中间件必须实现 Viswoole\Core\Contract\MiddlewareInterface,核心逻辑写在 process() 方法:
namespace App\Middleware;
use Closure;
use Override;
use Viswoole\Core\Contract\MiddlewareInterface;
class AuthMiddleware implements MiddlewareInterface
{
/**
* @param RequestInterface $request 请求实例(构造参数由容器注入)
* @param ResponseInterface $response 响应实例
*/
public function __construct(
protected RequestInterface $request,
protected ResponseInterface $response
)
{
}
/**
* @param Closure $handler 下一个中间件的处理闭包
*/
#[Override] public function process(Closure $handler): mixed
{
// 未携带令牌则直接短路返回,不再调用 $handler
if (empty($this->request->getHeader('authorization'))) {
return ['code' => 401, 'message' => '请先登录'];
}
return $handler();
}
}构造参数由容器自动解析;类中间件实例每个请求新建,可在构造器中注入请求级依赖:
use Viswoole\Core\Facade\Middleware;
Middleware::register(AuthMiddleware::class, 'http');3. 类中间件 + 显式构造参数
需要向构造函数传入参数时使用 [类名, ['构造参数名' => 值]] 格式。显式传入的参数与容器自动解析的依赖按名称合并,未传入的仍由容器注入;类若定义了 public static factory() 则按容器 make 语义注入 factory 方法:
use Viswoole\Core\Facade\Middleware;
// 实例化 RateLimitMiddleware(['limit' => 10]),其余构造参数仍由容器解析
Middleware::register([RateLimitMiddleware::class, ['limit' => 10]], 'http');WARNING
[对象实例, ['参数' => 值]] 不受支持——实例已创建无法再传构造参数,注册时会直接抛出异常。可调用形式仅支持 [类或实例, '方法名'] 与全局函数名等标准 callable。
执行顺序
请求分发时中间件按以下顺序合并成一条管道:
- 全局中间件:
Middleware::register($handler)注册 - 服务级中间件:
Middleware::register($handler, $serverName)注册 - 路由级中间件:路由实例的
setMiddlewares(),或注解路由的middlewares参数(组级中间件会被子路由继承)
请求 → 全局中间件 → 服务级中间件 → 路由级中间件 → 路由处理器
响应 ← 全局中间件 ← 服务级中间件 ← 路由级中间件 ← 处理器返回同层级内按注册/声明顺序执行。
路由级中间件
// 编程式:链式追加(可多次调用,追加合并)
Router::get('/user/{id}', [UserController::class, 'detail'])
->setMiddlewares([AuthMiddleware::class]);
// 注解式:Controller 类级 + RouteMapping 方法级,方法级追加在类级之后
#[Controller(prefix: 'user', middlewares: [LogMiddleware::class])]
class UserController
{
#[RouteMapping(method: 'GET', middlewares: [AuthMiddleware::class])]
public function detail(): array {}
// 执行顺序:LogMiddleware → AuthMiddleware
}分组中间件详见编程式路由。
内置跨域中间件
框架内置 Viswoole\Core\Middlewares\AllowCrossDomain:为所有响应添加 CORS 头,并对 OPTIONS 预检请求短路直接返回响应(不再进入路由处理器):
Access-Control-Allow-Origin: *Access-Control-Allow-Headers: *Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONSAccess-Control-Max-Age: 86400(预检结果缓存一天)
需要跨域支持时注册即可:
use Viswoole\Core\Middlewares\AllowCrossDomain;
use Viswoole\Core\Facade\Middleware;
Middleware::register(AllowCrossDomain::class, 'http');INFO
即使未注册跨域中间件,框架对 OPTIONS 预检请求也会跳过「请求方法不允许」校验并放行进入中间件管道,由跨域中间件负责短路响应,避免预检被 404 拦截。
