编程式路由
编程式路由指在路由定义文件(默认 config/route/route.php,可经 router.route_config_files 扩展)中通过 Router 门面静态调用注册路由。适合集中管理跨控制器的路由、全局兜底路由与快速原型。文件在服务启动时加载一次,修改后需重启服务。
注册基本路由
通过 Viswoole\Router\Facade\Router 门面,按 HTTP 方法注册路由,第一个参数支持传字符串或多路径数组:
<?php
// config/route/route.php
use App\Controller\UserController;
use Viswoole\Router\Facade\Router;
Router::get('/user/info', [UserController::class, 'info']); // GET /user/info
Router::post('/user/create', [UserController::class, 'create']); // POST /user/create
Router::put('/user/update', [UserController::class, 'update']); // PUT /user/update
Router::delete('/user/delete', [UserController::class, 'delete']);
Router::patch('/user/patch', [UserController::class, 'patch']);
Router::head('/user/head', [UserController::class, 'head']);
Router::options('/user/options', [UserController::class, 'options']);
Router::any('/ping', function () { // 任意方法
return ['pong' => true];
});快捷方法一览
| 方法 | 签名 | 说明 |
|---|---|---|
get | (string|array $paths, $handler): Route | 注册 GET 路由 |
post / put / delete / patch / head / options | 同上 | 注册对应方法的路由 |
any | (string|array $paths, $handler): Route | 不限制请求方法 |
addRoute | (string|array $paths, $handler, string ...$method): Route | 按传入方法名注册一条或多条 |
group | (string|array $prefix, Closure $closure, string $id): Group | 路由分组,$id 必填 |
miss | (Closure $handler, string|array $method = '*'): void | 兜底路由 |
server | (string $serverName, Closure $closure): void | 仅在指定服务运行时执行闭包内的路由注册 |
getRoute | (string $idOrCiteLink): Route|Group | 按 id 或引用链路查询路由实例 |
getRoutes | (): array | 获取全部顶层路由 |
INFO
同一路径按不同方法注册多条路由是合法的(RESTful 风格),匹配时按请求方法选择处理器;仅当同一路径且同一方法重复定义时才会抛出路由 id 冲突异常。
处理器的五种形式
$handler 支持以下写法,调用时统一经容器解析并注入依赖:
use App\Controller\UserController;
use Viswoole\HttpServer\Contract\RequestInterface;
// 1. 闭包(参数由容器自动注入,如 RequestInterface)
Router::get('/time', function (RequestInterface $request) {
return ['path' => $request->getPath()];
});
// 2. '类@方法' 字符串
Router::get('/user/info', 'App\Controller\UserController@info');
// 3. [类名, 方法名] 数组(推荐,静态分析友好)
Router::get('/user/list', [UserController::class, 'list']);
// 4. '类::方法' 字符串(适用于静态方法)
Router::get('/config/all', 'App\Controller\ConfigController::all');
// 5. 全局函数名
Router::get('/health', 'app_health_check');处理器返回值如何转为 HTTP 响应(数组自动 JSON、ResponseInterface 直接发送等)见响应对象。
动态路由参数
必选与可选参数
路径中使用 {参数名} 声明必选参数,{参数名?} 声明可选参数(缺失时由处理器方法参数默认值兜底):
// GET /user/1024 → $id = '1024';GET /user/ → 404
Router::get('/user/{id}', [UserController::class, 'detail']);
// GET /list → $page = 1;GET /list/3 → $page = '3'
Router::get('/list/{page?}', [UserController::class, 'list']);动态参数会按名注入处理器的同名方法参数(如 /user/1024 传入 detail(int $id))。
setPatterns 正则约束
用 setPatterns() 限定变量格式,正则不匹配则该路由不命中:
Router::get('/article/{category}/{id}', [ArticleController::class, 'show'])
->setPatterns([
'category' => '[a-z]+',
'id' => '\d+',
]);约束规则:
- 键为变量名,值为非空且合法的正则字符串(注册时校验,非法直接抛异常)
- 未约束的变量使用全局
router.default_pattern_regex(默认[\w\.]+) - 变量名必须以字母或下划线开头、仅含字母数字下划线、不超过 32 字符,同一路径内不可重复
路由分组
Router::group() 把一批路由组织到统一前缀下,第三个参数 $id必填(非注解路由框架无法生成稳定 id,需手动指定且不可重复):
Router::group('/api/v1', function () {
// 相对路径:与分组前缀拼接
Router::get('user/info', [UserController::class, 'info']);
}, 'api.v1');
// 实际路由: GET /api/v1/user/info路径合并规则
子路由是否以 / 开头决定与前缀的合并方式:
| 子路由写法 | 语义 | 合并结果(前缀 /api/v1) |
|---|---|---|
user/info | 相对路径 | /api/v1/user/info(拼接前缀) |
/user/info | 绝对路径 | /user/info(忽略前缀) |
/ | 前缀本身 | /api/v1(分组默认入口) |
嵌套分组遵循同样规则,内层分组相对路径(如 'v1')会与外层前缀拼接:
Router::group('/api', function () {
Router::group('v1', function () {
Router::get('user/info', [UserController::class, 'info']);
}, 'api.v1');
}, 'api');
// 实际路由: GET /api/v1/user/infoWARNING
分组的链式设置器(如 setMiddlewares())应在 group() 返回值上、闭包定义之后直接链式调用。分组内子路由在路由解析阶段才实例化,此时会继承分组已设置的属性(中间件、域名、后缀等)。
兜底路由(miss)
所有路由均未匹配(404、方法不允许等)时执行 miss 回调,$method 参数可为特定请求方法注册专属兜底;匹配顺序为「精确方法 → '*'」:
Router::miss(function () {
return ['code' => 404, 'message' => 'Not Found'];
});
// 仅为 GET 请求注册兜底
Router::miss(function () {
return ['code' => 404, 'message' => '资源不存在'];
}, 'GET');miss 只接受闭包;需要复用控制器逻辑时,在闭包中调用控制器方法即可。
服务级路由
Router::server() 内的路由定义仅在对应名称的服务运行时生效,适合多服务共用一份路由文件(服务名不区分大小写):
Router::server('http', function () {
Router::get('/', [HomeController::class, 'index']);
});链式设置器
get/post/... 返回 Route 实例、group 返回 Group 实例,两者共享一批链式设置器(均返回 $this):
| 方法 | 说明 |
|---|---|
setMethod(string|array ...$method) | 覆盖允许的请求方法 |
setMiddlewares(array $middlewares) | 追加中间件(校验合法性) |
setPatterns(array $patterns) | 追加动态变量正则约束 |
setMeta(array $meta) | 追加自定义元数据(关联数组) |
setSuffix(string ...$suffix) | 覆盖伪静态后缀 |
setDomain(string ...$domain) | 覆盖生效域名 |
setTitle(string $title) | 设置文档标题 |
setDescription(string $description) | 设置文档描述 |
setHidden(bool $flag) | 是否在 API 文档中隐藏 |
setSort(int $sort) | 排序,越大越靠前 |
Route 额外支持文档维护设置器:setTags(string ...$tag)、setStatus(Status $status)、setAuthor()、setCreatedAt()、setUpdatedAt()。
Router::get('/user/{id}', [UserController::class, 'detail'])
->setPatterns(['id' => '\d+'])
->setMiddlewares([AuthMiddleware::class])
->setTitle('用户详情')
->setSort(10);完整示例
<?php
// config/route/route.php
use App\Controller\UserController;
use App\Middleware\AuthMiddleware;
use Viswoole\Router\Facade\Router;
// API 分组
Router::group('/api/v1', function () {
// 用户模块(需认证)
Router::group('user', function () {
Router::get('info', [UserController::class, 'info']);
Router::get('{id}', [UserController::class, 'detail'])
->setPatterns(['id' => '\d+']);
Router::post('create', [UserController::class, 'create']);
}, 'api.v1.user')->setMiddlewares([AuthMiddleware::class]);
}, 'api.v1');
// 首页(绝对路径路由)
Router::get('/', [UserController::class, 'home']);
// 404 兜底
Router::miss(function () {
return ['code' => 404, 'message' => '页面不存在'];
});