我来仔细分析一下你的设计方案,看看有哪些需要考虑的地方:
1. 核心执行流程设计
当前设计的关键问题:
中间件和Handler的返回值语义不一致:
Middleware是(Context, Next) -> Unit,通过调用next()来传递控制Handler是(Context) -> Any?,通过返回值来处理这会导致:中间件如何"提前返回"不够清晰
建议考虑:
// 方案1: 统一为类似Koa的async/await模式(但Kotlin用suspend)
typealias Middleware = suspend (Context, Next) -> Unit
typealias Next = suspend () -> Unit
// 方案2: 通过Context的状态来标识是否继续
// 中间件设置了response.body后,自动跳过后续路由匹配"提前返回"的语义需要明确:
是指:设置了
response.body就不再执行后续handler吗?还是说:中间件不调用
next()就停止?如果handler也想"提前终止后续中间件"怎么办?
2. 路由匹配优先级
需要明确的规则:
app.post("/users/123", handler1) // 具体路径
app.post("/users/{id}", handler2) // 参数路径
app.use("/users", middleware) // 前缀中间件执行顺序应该是什么?
建议:按注册顺序 + 具体度排序
先执行所有匹配的中间件(按注册顺序)
再匹配路由handler(精确匹配 > 参数路径,相同优先级按注册顺序)
路径匹配复杂度:
app.use("/users/{id}", middleware)
app.get("/users/{id}/posts", handler)如何处理嵌套的路径参数?
/users/{id}的中间件是否匹配/users/123/posts?(应该匹配)
3. 子App挂载(mount)的边界情况
需要考虑:
app.mount("/admin", adminApp)路径参数传递:
app.mount("/users/{userId}/admin", adminApp)
adminApp.get("/posts", handler)
// 访问 /users/123/admin/posts 时,handler能获取到userId吗?中间件作用域:
app.use(globalMiddleware) // 全局
app.mount("/admin", adminApp) // adminApp内部的请求要经过globalMiddleware吗?
adminApp.use(adminMiddleware) // 仅admin内部子App的独立性:
子App内部看到的
ctx.path是完整路径还是去掉前缀的路径?建议:去掉前缀,这样adminApp可以独立开发
父App的
state会传递给子App吗?
4. Response.Body 的状态管理
sealed class Body {
object Empty : Body() // 你的TODO很关键!
// ...
}需要区分三种状态:
未设置(初始状态)- 应该继续路由匹配
设置为Empty(显式空响应)- 不应该继续匹配?
设置了内容 - 不应该继续匹配
建议:
sealed class Body {
object Unset : Body() // 未设置,继续路由
object Empty : Body() // 显式空响应(204 No Content?)
data class Text(val data: String) : Body()
// ...
}5. 错误处理和404
当前设计没有涉及:
所有路由都不匹配时,如何返回404?
中间件或handler抛出异常时,如何处理?
是否需要错误处理中间件?
// 类似Express的错误处理
typealias ErrorMiddleware = (Throwable, Context, Next) -> Unit
app.onError(errorMiddleware)6. 条件中间件的执行时机
app.useIf(predicate, middleware)predicate在什么时候执行?每次请求都要判断吗?如果predicate依赖context,那它实际上和普通中间件差别不大:
app.use { ctx, next -> if (predicate(ctx)) { middleware(ctx, next) } else { next() }}
7. 动态添加路由的线程安全
运行时动态添加路由和中间件
这意味着可能在处理请求的同时修改路由表
需要考虑并发安全:
使用
CopyOnWriteArrayList?还是加读写锁?
还是每次请求时快照路由表?
8. 路径规范化
app.get("/users/", handler)
app.get("/users", handler)这两个是否应该视为相同?
/users//profile和/users/profile呢?如何处理
..、.等?
9. 中间件链的完整性
app.use { ctx, next ->
println("before")
next() // 如果忘记调用next()会怎样?
println("after")
}Koa的洋葱模型关键在于:中间件可以在
next()前后执行代码你的设计是否支持这个?是否需要
suspend来保证?
10. 性能考虑
每次请求都要遍历所有中间件和路由吗?
是否需要路由树(Trie)来优化匹配?
路径参数的正则匹配性能如何?
总结建议
最关键的设计决策:
明确"提前返回"的语义 - 这是整个框架的核心
确定执行流程 - 中间件→路由匹配→handler,何时停止?
定义清晰的优先级规则 - 注册顺序、路径具体度、精确匹配vs参数匹配
sub-app的边界 - 路径、参数、状态的作用域
建议先定义:
// 请求的生命周期
1. 匹配并收集所有相关中间件(按优先级)
2. 匹配路由handler
3. 构建执行链(中间件链 + handler)
4. 执行链(洋葱模型)
5. 如果body未设置 → 404
6. 异常处理设计看起来很棒,把这些细节考虑清楚后实现会更顺畅!