webman路由必须集中定义在config/route.php中,修改后需重启服务;中间件process()必须接收$request和$next两个参数并显式调用$next($request),返回值须为response实例;options请求需单独注册路由或使用route::any()。

Webman 的路由和中间件不是“配完就跑”,不理解 FastRoute 编译时机、中间件洋葱模型和协程上下文,90% 的问题都出在配置生效逻辑或参数传递链上。
Route::get() 等静态方法必须写在 config/route.php 里
框架启动时只加载 config/route.php,其他文件里的 Route::get() 不会被识别。常见错误是把路由写进控制器、中间件甚至自定义 PHP 文件里,结果请求 404 却查不到原因。
- 所有路由定义必须集中到
config/route.php,不能拆到子目录下靠 require_once 自动加载(除非你手动遍历 glob 并require_once) -
Route::get('/user', [...])中的数组必须是[类名::class, '方法名']格式,写成字符串'app\controller\UserController@index'会报类未找到 - 修改路由后必须重启服务(
php webman start -d),热重载不生效——FastRoute 的路由表是在进程启动时一次性编译的
中间件 process() 方法漏掉 $next 或返回非 Response 实例
这是最隐蔽的“静默失败”:路由能匹配、控制器不执行、日志没输出、也不报错,只因中间件卡在了洋葱皮第一层。
-
process()必须接收两个参数:$request和$next;少一个,框架直接跳过该中间件 - 必须显式调用
$next($request)才能往下传,不调就断链;想拦截就return response('', 401),但不能 return 字符串或数组 - 返回值必须是
Webman\Http\Response实例,json([...])或response()->json(...)都不行——除非你确认它内部已封装为 Response 对象 - 若用
$request->withAttribute('user', $user)往请求挂数据,后续控制器要用$request->getAttribute('user')取,别试图从 $_SESSION 或全局变量读
OPTIONS 预检请求 404 是因为没注册对应路由
CORS 中间件本身不会让 OPTIONS 请求进来——如果路由没定义,请求连中间件的边都碰不到。
- 浏览器发 OPTIONS 时,
Route::post('/api/login', ...)完全不匹配,必须显式加Route::options('/api/login', ...)或统一用Route::any('/api/login', ...) - 中间件里判断
if ($request->method() === 'OPTIONS')后,必须return response('', 204)并带上 CORS 头,不能只withHeader()然后继续$next - 如果用了 Nginx 反代,
$request->ip()拿到的是127.0.0.1,限流或审计要改用$request->header('x-real-ip')
多应用路由自动加载需手动 require glob 结果
Webman 不会自动扫描 app/*/route.php,所谓“多应用路由拆分”是靠你在 config/route.php 里手写加载逻辑实现的。
- 在
config/route.php开头加上:$routes = glob('app/*/route.php'); foreach ($routes as $file) { require_once $file; } - 每个子应用的
route.php里不能再写<?php前置声明,否则报语法错;也别再重复use Webman\Route; - 子路由文件中定义的
Route::group('/admin', ...)会叠加到主路由表,但中间件绑定只在当前文件作用域有效,跨文件不继承 - 如果某个子应用的路由没生效,先
var_dump($routes)看 glob 是否真扫到了文件路径,再检查文件权限和 PSR-4 命名空间是否一致
真正容易被忽略的是:所有中间件实例在常驻进程中复用,__construct() 里初始化的资源(比如 Redis 连接、Logger)必须协程安全;而路由参数、用户身份、真实 IP 这些动态值,只能在 process() 内实时取,不能缓存在属性里跨请求复用。










