我来仔细分析一下你的设计方案,看看有哪些需要考虑的地方:

1. 核心执行流程设计

当前设计的关键问题:

建议考虑:

// 方案1: 统一为类似Koa的async/await模式(但Kotlin用suspend)
typealias Middleware = suspend (Context, Next) -> Unit
typealias Next = suspend () -> Unit

// 方案2: 通过Context的状态来标识是否继续
// 中间件设置了response.body后,自动跳过后续路由匹配

"提前返回"的语义需要明确

2. 路由匹配优先级

需要明确的规则:

app.post("/users/123", handler1)      // 具体路径
app.post("/users/{id}", handler2)     // 参数路径
app.use("/users", middleware)         // 前缀中间件

执行顺序应该是什么?

路径匹配复杂度:

app.use("/users/{id}", middleware)
app.get("/users/{id}/posts", handler)

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的独立性:

4. Response.Body 的状态管理

sealed class Body {
    object Empty : Body()  // 你的TODO很关键!
    // ...
}

需要区分三种状态:

  1. 未设置(初始状态)- 应该继续路由匹配

  2. 设置为Empty(显式空响应)- 不应该继续匹配?

  3. 设置了内容 - 不应该继续匹配

建议:

sealed class Body {
    object Unset : Body()      // 未设置,继续路由
    object Empty : Body()      // 显式空响应(204 No Content?)
    data class Text(val data: String) : Body()
    // ...
}

5. 错误处理和404

当前设计没有涉及:

// 类似Express的错误处理
typealias ErrorMiddleware = (Throwable, Context, Next) -> Unit
app.onError(errorMiddleware)

6. 条件中间件的执行时机

app.useIf(predicate, middleware)

7. 动态添加路由的线程安全

运行时动态添加路由和中间件

8. 路径规范化

app.get("/users/", handler)
app.get("/users", handler)

9. 中间件链的完整性

app.use { ctx, next ->
    println("before")
    next()  // 如果忘记调用next()会怎样?
    println("after")
}

10. 性能考虑

总结建议

最关键的设计决策:

  1. 明确"提前返回"的语义 - 这是整个框架的核心

  2. 确定执行流程 - 中间件→路由匹配→handler,何时停止?

  3. 定义清晰的优先级规则 - 注册顺序、路径具体度、精确匹配vs参数匹配

  4. sub-app的边界 - 路径、参数、状态的作用域

建议先定义:

// 请求的生命周期
1. 匹配并收集所有相关中间件(按优先级)
2. 匹配路由handler
3. 构建执行链(中间件链 + handler)
4. 执行链(洋葱模型)
5. 如果body未设置 → 404
6. 异常处理

设计看起来很棒,把这些细节考虑清楚后实现会更顺畅!