跳转至

web.router

web 路由:值语义 Router + 静态段 > :param > *splat 匹配 + 405 Allow + 中间件平铺链 + dispatch + not_found/method_not_allowed 覆写挂点(2b Task 2) 设计: docs/superpowers/specs/2026-09-27-web-framework-design.md §4.1-§4.2;纪律 §14-1 serve 循环(test_call 本体)归 2b,直接复用 dispatch。

引导 interp 口径实测约定(实证见 task-3-report): 1. 泛型 struct 字面量一律裸形(Route { ... });Route[I64] { ... } 不入解析。 2. 泛型 fn 不得作值引用(no_h[S] 形 → E2020 S 未解析)——占位 handler 用闭包字面量。 3. 实参含 fn 型(内提 S)或调用方自身泛型时,调用一律显式 TypeArgs(run_chain[S](...)), 泛型 struct 型实参不参与推断(E2060);具体类型语境(测试体)UFCS .get/.middleware 可由接收者推断,冻结名面照用。

pub struct

struct RouteSeg

Field Type
kind Str
text Str

struct Route[S]

Field Type
m Str
segs List[RouteSeg]
chain List[fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp]
h fn(&Req[S]) -> Resp

struct Router[S]

Field Type
state S
routes List[Route[S]]
mws List[fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp]
not_found_h List[fn(&Req[S]) -> Resp]
not_allowed_h List[fn(&Req[S]) -> Resp]

struct Match[S]

Field Type
kind I32
route Route[S]
params List[Pair]
splat Str
allow Str

struct Pair

Field Type
k Str
v Str

struct Resp

Field Type
status I32
headers List[Pair]
body Str

struct JObj

Field Type
parts List[Str]

struct Req[S]

Field Type
method Str
path Str
query Str
body Str
headers List[Pair]
cookies List[Pair]
params List[Pair]
session List[Pair]
state S

pub fn

Signature Returns Description
router[S](state: S) Router[S] —
get[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
post[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
put[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
delete[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
patch[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
middleware[S](r: Router[S], mw: fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp) Router[S] 组级中间件:声明序 = 执行序(链头先跑);组内全部路由生效——已注册路由的预拼链
亦回填追加(值语义:链不可变,重铸每条),后续注册路由经 r.mws 继承
mount_at[S](r: Router[S], prefix: Str, sub: Router[S]) Router[S] 子路由器前缀挂载:每条子路由预拼前缀段(最终一张平铺表,热路径无嵌套);
中间件继承:子组中间件已随子路由注册预拼入逐路由链(合并时 chain = 宿主 mws + 子链,
执行序外层→内层→handler §4.1);合并态 mws 仅取宿主——子组 mw 不入合并态列表,
否则挂载后新注册的宿主路由经 r_add 继承之、嵌套兄弟组经外层合并互渗(双外泄向量)。
state 取宿主(单一应用状态)。同样启动期查重(前缀拼接后即最终形态)。
【钉注 2b Task 3】404/405 覆写钩子同 mws 口径:合并态仅取宿主槽——子路由器的
not_found_h/not_allowed_h 挂载时丢弃(host-level 404/405 语义:整个应用一张
错误页策略;钉死测试见「mount_at 丢弃子路由覆写钩子」)。子组 404 定制属志向。
has_conflict[S](r: Router[S]) Bool 启动自检面(2b serve 前置调用):表内存在重复 (method, 模式) 即 true
install[S](r: Router[S], p: fn(Router[S]) -> Router[S]) Router[S] 插件挂载(§4.6:插件 = 普通 fn(Router[S]) -> Router[S],零新机制;顺序即装配序)
not_found[S](r: Router[S], h: fn(&Req[S]) -> Resp) Router[S] —
method_not_allowed[S](r: Router[S], h: fn(&Req[S]) -> Resp) Router[S] —
test_call[S](r: Router[S], method: Str, path: Str, body: Str) Resp test_call —— spec §4.7 测试口(dispatch 别名;整台服务器零 socket 的确定性测试形;
2b serve 循环复用 dispatch 本体)
route_match[S](r: Router[S], m: Str, path: Str) Match[S] —
dispatch_h[S](r: Router[S], method: Str, path: Str, body: Str, hdrs: List[Pair]) Resp dispatch_h —— 带头变体(2b Task 3 收口):hdrs 进 Req(headers 入 Req.headers,
Cookie 在 req_full 内解析 → 会话注入随 mw 链可达 handler);404/405 覆写臂同收
headered Req(task-2 交接①)。查询串:匹配面剥 "?query"(target 全形不参与段匹配),
req_full 仍收全 target(其内自分裂 path/query)——dispatch 旧形不剥,查询串曾恒 404,
此为纯增量修正(既有测试路径无查询串,零扰动)。
dispatch[S](r: Router[S], method: Str, path: Str, body: Str) Resp —
html(body: Str) Resp —
json(body: Str) Resp —
text(body: Str) Resp —
redirect(loc: Str) Resp —
status(n: I32, body: Str) Resp —
xml(body: Str) Resp —
yaml(body: Str) Resp —
err_json(n: I32, msg: Str) Resp —
attachment(r: Resp, filename: Str) Resp —
with(r: Resp, k: Str, v: Str) Resp —
with_status(r: Resp, n: I32) Resp —
resp_header(r: Resp, k: Str) Str —
resp_body(r: Resp) Str —
resp_status(r: Resp) I32 —
json_obj() JObj —
str(b: JObj, k: Str, v: Str) JObj —
i64(b: JObj, k: Str, v: I64) JObj —
bool(b: JObj, k: Str, v: Bool) JObj —
strs(b: JObj, k: Str, xs: List[Str]) JObj —
end(b: JObj) Str —
end_status(b: JObj, n: I32) Resp —
csv_rows(headers: List[Str], rows: List[List[Str]]) Str —
req_of[S](method: Str, path: Str, state: S) Req[S] —
req_full[S](method: Str, path: Str, body: Str, hdrs: List[Pair], state: S) Req[S] —
req_set_params[S](r: &Req[S], params: List[Pair]) Req[S] —
req_set_session[S](r: &Req[S], sess: List[Pair]) Req[S] —
pdec(s: Str) Str —
param[S](r: &Req[S], name: Str) Str —
pair_get(ps: List[Pair], k: Str) Str —
param_i64[S](r: &Req[S], name: Str) Option[I64] —
param_f64[S](r: &Req[S], name: Str) Option[F64] —
query[S](r: &Req[S], name: Str) Str —
query_all[S](r: &Req[S], name: Str) List[Str] —
form[S](r: &Req[S], name: Str) Str —
form_all[S](r: &Req[S], name: Str) List[Str] —
header[S](r: &Req[S], name: Str) Str —
cookie[S](r: &Req[S], name: Str) Str —
session[S](r: &Req[S], name: Str) Str —