webman中间件需严格遵循process(request $request, callable $next)签名,$next必须显式调用,返回response实例,且需注意协程安全、注册方式、路由匹配时机及性能优化。

process() 方法签名必须是 process(Request $request, callable $next)
Webman 中间件不是“写完放那就行”的组件,它靠函数签名驱动整个洋葱模型。漏掉 $next 或类型写错(比如写成 $handler),框架不会报错,但请求会在该中间件戛然而止——控制器不执行、日志没输出、响应卡住,你却找不到原因。
-
$request是Webman\Http\Request实例,可直接调用$request->get()、$request->header('X-Token')、$request->path()等方法 -
$next是后续链的 callable,必须显式调用$next($request)才能往下走;想拦截就直接return response('Unauthorized', 401),千万别在 return 前还调$next() - 返回值必须是
Webman\Http\Response实例,不能是字符串、数组、json_encode()结果或未封装的response()->json()调用(除非你确认它内部已返回 Response 对象) - 协程安全:不要往
$_SESSION、全局变量或静态属性里存用户数据;要用$request->withAttribute('user', $user)挂载,下游通过$request->getAttribute('user')获取
config/middleware.php 全局注册与 config/route.php 路由绑定的区别
全局中间件对所有匹配路由生效,包括 /public/* 静态资源和 /health 探针接口——这容易引发误拦截或性能浪费。实际项目中,更推荐按需绑定。
- 全局注册写在
config/middleware.php的'global'键下:['global' => [app\middleware\AuthMiddleware::class]] - 路由级绑定写在
config/route.php的路由定义中:'middleware' => [app\middleware\AuthMiddleware::class],支持数组叠加多个 - 分组绑定更高效:
Route::group('/api', function () { ... })->middleware(app\middleware\ApiAuth::class),避免单个路由重复写 - 注意命名空间必须完整,文件路径必须符合 PSR-4:类
app\middleware\AuthMiddleware必须对应app/middleware/AuthMiddleware.php,拼错一个字母就静默失效
php webman route:list 查看中间件是否真正挂载
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
中间件写了、注册了、路由也配了,但就是不触发?别猜,先看事实。
- 运行
php webman route:list,检查目标路由行的Middlewares列是否列出你的中间件类名 - 如果为空,说明:路由没进分组、
config/route.php没被config/bootstrap.php加载、或者中间件类名写成AuthMiddleware::class(缺命名空间) - 如果显示的是类名缩写(如
AuthMiddleware),说明注册时用了字符串而非 FQCN,Webman 无法自动加载,会跳过 - 若使用插件,要确认插件自己的
config/middleware.php是否存在,且'web'或'api'分组已包含该中间件
process() 里读不到 $request->controller 和 $request->action?
默认情况下,$request->controller 和 $request->action 在中间件执行时尚未解析出来——它们是路由匹配后、控制器调用前才注入的。想基于控制器/动作做条件判断,得换方式。
- 方案一:用
$request->path()+ 正则匹配,例如preg_match('#^/admin/.*#', $request->path()) - 方案二:在路由定义时提前打标:
Route::get('/user/profile', [...])->name('user.profile'),然后在中间件里用$request->route()->getName() - 方案三:自定义路由参数,在
Route::get('/{controller}/{action?}', [...])中提取,但会破坏 RESTful 风格,慎用 - 不要依赖
$request->controller做鉴权判断,它在中间件阶段大概率为null,这是最常踩的坑之一
中间件嵌套超过 5 层会明显拖慢高频接口,尤其在 P95 延迟敏感场景。合并逻辑比堆砌中间件更可靠——比如把 token 解析、租户识别、权限校验全塞进一个 TenantAuthMiddleware,而不是拆成三个。另外,@no-inject 注解能跳过无依赖中间件的反射初始化,这点容易被忽略。










