Archived
app:路由注册表 —— 评审反馈修复(三个 Important findings)
补充了三处测试密度不足的地方: ① has/lookup 区分(Finding 1): 参数名换一个时,lookup 仍能匹配(参数段认任意值),has 说没有。 两个接口的语义差异这次被真的逼出来。 ② 字面优先级压力测试(Finding 2): 注册 /api/gov/:zone 和 /api/gov/staff,两条都能匹配同一个路径时, 验证字面优先于参数段这个优先级是否真的生效。 ③ 建表自检防卫(Finding 3): 两条参数段模版互撞时,命中谁只看声明顺序 —— 静默判错权限。 添加 canCollide() 函数与入口自检,撞了就 build fail loud。 验证:真实 95 条路由建得起来(no collision)。 测试统计:9 → 18 条(+9),全量 1757 → 1766(+9),门禁全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015kAzS35kMnZNt4YGgJXtfw
This commit is contained in:
@@ -53,6 +53,21 @@ function matcherOf(pattern: string): RegExp {
|
||||
return new RegExp(`^${body}$`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 两条模版会不会匹配到同一个具体路径:同段数,且每一位都兼容
|
||||
* (参数段与任何段兼容;字面段只与相同的字面兼容)。
|
||||
*/
|
||||
function canCollide(a: string, b: string): boolean {
|
||||
const sa = a.split('/');
|
||||
const sb = b.split('/');
|
||||
if (sa.length !== sb.length) return false;
|
||||
return sa.every((seg, i) => {
|
||||
const other = sb[i] ?? '';
|
||||
if (seg.startsWith(':') || other.startsWith(':')) return true;
|
||||
return seg === other;
|
||||
});
|
||||
}
|
||||
|
||||
export function buildRouteRegistry(routes: readonly RegisteredRoute[]): RouteRegistry {
|
||||
const literal = new Map<string, RouteAccess>();
|
||||
const patterns: Array<{ method: string; pattern: string; re: RegExp; access: RouteAccess }> = [];
|
||||
@@ -60,6 +75,26 @@ export function buildRouteRegistry(routes: readonly RegisteredRoute[]): RouteReg
|
||||
if (r.path.includes(':')) patterns.push({ method: r.method, pattern: r.path, re: matcherOf(r.path), access: r.access });
|
||||
else literal.set(`${r.method} ${r.path}`, r.access);
|
||||
}
|
||||
|
||||
/*
|
||||
* 两条模版能匹配到同一个具体路径 = 查它时命中谁全看声明顺序 —— 那是**静默判错权限**,
|
||||
* 比启动失败严重得多。契约是我们自己写的,撞了就是写错了:开发期就掀桌子。
|
||||
* 只检模版之间;字面与参数并存是**有意的**优先级(字面赢),不算冲突。
|
||||
*/
|
||||
for (let i = 0; i < patterns.length; i += 1) {
|
||||
for (let j = i + 1; j < patterns.length; j += 1) {
|
||||
const a = patterns[i];
|
||||
const b = patterns[j];
|
||||
if (a === undefined || b === undefined) continue;
|
||||
if (a.method === b.method && canCollide(a.pattern, b.pattern)) {
|
||||
throw new Error(
|
||||
`路由归属表冲突:${a.method} ${a.pattern} 与 ${a.method} ${b.pattern} 会匹配到同一个具体路径 —— ` +
|
||||
'命中谁只看声明顺序,等于静默判错权限。改掉其中一条的路径。',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
size: routes.length,
|
||||
lookup(method, path) {
|
||||
|
||||
Reference in New Issue
Block a user