GPT
整体看下来,你这个框架完成度已经很高了,而且不是“玩具级”的:
有 Koa 风格的 middleware/next 语义
有 Controller 注解 + 函数式路由
有 子应用 mount + 上下文继承
有 返回值自动映射 Response
有 Undertow + 虚拟线程 + SSE
还考虑了 Java 友好性
下面我从架构层面 → API 设计 → 运行时模型 → Undertow 适配几个维度,给你一个偏“框架作者视角”的 review,重点说「是否合理」「潜在坑」「可以进化的方向」,不会纠结代码风格小问题。
一、整体架构评价(一句话结论)
这是一个“非常像 Koa,但语义更接近 Spring/Javalin 混合体”的 Kotlin Web Framework,而且你的核心抽象是正确的。
尤其是这几点设计,我认为是 “对的”:
Handler(ctx): Any?+ 统一返回值映射middleware 使用
(ctx, next)而不是链式返回Context 是“能力聚合器”,而不是 Request/Response 暴露一堆 API
子应用 mount 不是 router hack,而是真正的 App 递归
Undertow adapter 没把 Undertow 的概念污染到 core
这已经超过 80% “自制框架”的水准了 👍
下面逐块分析。
二、Core 抽象设计(Handler / Middleware / ErrorHandler)
1️⃣ Handler 返回 Any? ——这是大胆但合理的设计
fun interface Handler {
operator fun invoke(ctx: Context): Any?
}你在 Response.applyHandlerResult() 里集中处理返回值,这一点非常关键:
框架层只认 Response 语义
handler 不关心 HTTP 细节(像 Koa)
又比 Koa 更“静态友好”
你这里做得比很多 Java 框架好的一点是:
❗禁止返回内部类型
private fun forbidInternalTypes(value: Any)这是一个成熟框架才会想到的保护措施,👍
否则用户一不小心 return ctx、return request,API 就会开始变形。
⚠️ 一个潜在问题:null 被禁止
null -> error("Handler returned null...")这是合理的,但会和 Kotlin 的表达式语义冲突:
app.get("/") {
if (cond) "ok" else null // ❌
}👉 建议(不强制)
保留现在的行为(我支持)
但可以在文档里明确:
handler 的返回值必须是 明确语义的
你已经通过异常强制用户“写清楚意图”,这是好事。
三、Middleware + 错误模型(这是你最强的一块)
2️⃣ Middleware 不是 try/catch,而是 对称执行模型
你在 Context.error 的注释里写了非常关键的一点:
before / after 一定成对执行,即使发生异常
这点在 Java/Kotlin 世界里是非常少见的正确实现方向,本质上你在做的是:
“结构化中间件执行模型(类似 try/finally)”
👍👍👍
⚠️ 但现在有一个「语义不完全显式」的问题
你现在是:
middleware / handler 抛异常
router 捕获
ctx.error保存最终统一 rethrow / handle
问题是:
middleware 如何“声明自己已经处理完异常”?
你提到了:
error.handled = true但目前在你给的代码里:
Throwable本身没有handledContext.error只是一个Throwable?
👉 强烈建议(重要)
引入一个明确的异常包装类型:
class ContextError(
val cause: Throwable,
var handled: Boolean = false
)Context 里用:
var error: ContextError? = null否则以后:
OpenAPI 插件
Logging middleware
Metrics middleware
都会开始“猜异常状态”,这是会烂的地方。
四、Sub-App Mount(这是你架构上最亮眼的部分)
3️⃣ mount(path, app) 设计是 正确且高级的
你不是简单地:
router 前缀拼接
handler list 复制
而是:
子 app 有独立 config / middleware / errorHandlers
Context 有
parentContext服务 & state 支持向上查找
path / pattern 语义清晰区分
这已经是:
Express / Koa / Javalin 都没完全做对的事情
⚠️ 一个非常容易踩坑的点:Response merge
internal fun merge(subResponse: Response)现在是:
this.status = subResponse.status
this.body = subResponse.body
this.headers[key] = values潜在问题
parent middleware 可能已经设置 header
sub-app 覆盖了它
行为顺序依赖 router 实现
👉 建议
在设计文档里明确一个原则:
sub-app 的 response 完全接管 response
或
sub-app 的 response 只是建议,parent 可以 override
两者都合理,但必须选一个,并写下来。
五、Request / Response 设计(非常接近“理想形态”)
4️⃣ Request 是“低层对象”,Context 是“能力门面” ——正确
你没有让用户直接操作 Undertow 的 Exchange也没有让 Request 膨胀成 God Object
👍
Request 的几个亮点
multipart 延迟解析(
multipartSupplier)lazyLoom(虚拟线程友好)json / form / query 统一 mapToClass
accepts/acceptsLang
这些设计已经是 生产级。
5️⃣ Response.applyHandlerResult ——你这个方法是“框架心脏”
这段代码非常重要,我评价它一句话:
逻辑是对的,但以后会成为扩展瓶颈
你现在支持的返回类型
Unit / Int / String / ByteArray / InputStream
Map / List → JSON
Result
ResponseBody
fallback JSON
这很好。
但以后你一定会想加:
HttpStatusenumEither<Error, T>Flow<T>/Sequence<T>文件下载(自动 Content-Disposition)
👉 建议未来重构方向(不是现在):
interface HandlerResultResolver {
fun supports(value: Any): Boolean
fun apply(value: Any, ctx: Context, response: Response)
}然后:
val resolvers = listOf(
PrimitiveResolver,
JsonResolver,
ResultResolver,
...
)你现在的实现 完全可以作为 default resolver,只是提前给你一个演进路线。
六、Undertow 适配层(写得非常专业)
6️⃣ Undertow Adapter 是「合格的 server adapter」
你这里做对了很多人会做错的事:
✅ 线程模型清晰
IO thread ≠ business thread
virtual threads 模式下绕开 Undertow worker pool
SSE 单独 executor
✅ 生命周期顺序正确
shutdown:
1. gracefulShutdown
2. stop accepting
3. wait http
4. wait virtual threads
5. stop server这已经是 企业级顺序 了。
⚠️ 一个细节建议(小)
exchange.startBlocking()你是 无条件调用 的,这意味着:
即使是 GET / 无 body
也进入 blocking 模式
👉 建议:
只在需要 body / multipart 时调用
或在 RequestAdapter 里延迟调用
这在高并发下能少一点阻塞切换。
七、总体建议(非常重要)
如果你继续做这个框架,我的 3 个建议优先级是:
🥇 第一优先级:把异常模型“显式化”
ContextErrorhandled / propagated
middleware / handler 语义明确
🥈 第二优先级:写设计文档而不是功能
你这个框架已经到:
❌ 再加功能 = 风格开始乱✅ 写清楚「我是谁」「我不像谁」
比如:
为什么禁止 handler 返回 null
middleware 执行模型
sub-app 的 response 语义
🥉 第三优先级:为扩展留钩子,不急着实现
handler result resolver
route added hook(你之前问的 OpenAPI,非常适合)
middleware lifecycle events
最后一句评价
这不是“我用 Kotlin 写了个 web 框架”,而是“我在设计一个有清晰哲学的 Web Runtime”。
如果你愿意,下一步我可以帮你:
把这个框架的 设计哲学写成 README
或 对比 Koa / Javalin / Spring WebFlux 的差异定位
或直接 review Router / Middleware 执行器那一层
你现在已经站在“可以被认真 review”的层级了。