Tea 中间件机制深度解析:如何快速为仓颉 Web 框架打造请求拦截链
2026/9/24 14:26:52 网站建设 项目流程

Tea 中间件机制深度解析:如何快速为仓颉 Web 框架打造请求拦截链

【免费下载链接】tea仓颉语言轻量级的、函数式的、高效的HTTP Web后端框架项目地址: https://gitcode.com/Cangjie-SIG/tea

🍵Tea是一个用仓颉(Cangjie)语言编写的轻量级、函数式、高效的 HTTP Web 后端框架。它的中间件(Middleware)机制让"请求拦截链"变得极其简单——你只需几行代码,就能在任意路由前插入鉴权、日志、CORS、限流等逻辑,而完全不必污染业务 Handler。本文带你从原理到实战,一次看懂并亲手打造属于自己的请求拦截链。

核心关键词:Tea 中间件仓颉 Web 框架请求拦截链use 注册中间件CORS 中间件共享参数 set/get钩子函数 Hooks


一、什么是 Tea 中间件?先看懂"洋葱模型"

🧅 中间件的本质,是一层包裹在请求与响应外围的处理函数。一个请求进来后,会依次"剥开"多个中间件;当它最终到达业务 Handler 后又"逐层回包"——这就是经典的洋葱模型(Onion Model)

在 Tea 里,中间件和 Handler 是同一种东西

public type HandlerFunc = (TeaContext) -> Unit // 接收上下文,返回 Unit public open class Handler { ... } // 仿函数,可携带元信息 metaData
  • 每个中间件接收一个TeaContext(下文简称c),代表当前请求的完整上下文
  • 调用c.next()→ 放行,进入下一个中间件 / Handler。
  • 不调用c.next()→ 拦截链在此中断,请求不会继续往下走。

💡 一句话记忆:next()是油门,不调就是刹车。这是所有拦截能力(鉴权拒绝、限流、错误兜底)的来源。

中间件的入口定义在 src/core.cj,Handler作为仿函数还能携带metaData(一个HashMap),方便你给每个处理函数"打标签"。


二、如何注册中间件:use 方法的 3 种写法

Tea 用use方法注册中间件(实现在 src/tea.cj)。按作用范围,它有 3 种常见用法:

写法作用范围典型场景
app.use { ... }全部路由、全部方法全局日志、统一 CORS
app.use("/api", { ... })仅匹配路径前缀/api接口鉴权
group.use { ... }路由组内部版本分组/v1/v2独立策略

1. 全局中间件(最简单)

let app = Tea.default() // 为 app 下全部路由注册:任意请求都会先经过它 app.use { c => println(">>> 收到请求 ${c.getMethod()}") c.next() // 必须调用,否则会中断 }

2. 按路径前缀注册

// 只有 /admin 开头的路由才走这段中间件 app.use("/admin") { c => // 校验管理员身份,失败则不调用 c.next() 即可拦截 c.next() }

3. 路由组(Group)中间件

通过 src/group.cj 的use,给整个版本组挂一套独立策略,是组织多版本 API 的最佳实践:

let v1 = app.group("/api/v1") { c => c.set("version", "v1") // 给整组打标签 c.next() } v1.get("/list") { c => /* ... */ }

✅ 小提示:use注册的是"通配方法"路由,意味着它对 GET/POST/PUT 等所有 HTTP 方法都生效,无需为每个方法单独注册。


三、请求拦截链是如何执行的?next() 逐层拆解 🚀

很多人只记住了c.next(),却不知道它背后是两级推进。我们把执行引擎拆开看,你就彻底懂了。

第一级:TeaContext.next()(中间件内部推进)

源码见 src/context.cj:

public func next(): Unit { this.indexHandler ++ // 指针 +1 if (this.indexHandler < this.route.handlers.size) { this.route.handlersthis.indexHandler // 执行“下一个” return } this.app.next(this) // 本路由用完 → 交给 Tea }
  • 当前路由上挂着一串handlers(中间件 + 主 Handler 排成一队)。
  • 每调一次next()indexHandler前进一格,执行队列中的下一个函数

第二级:Tea.next()(路由之间推进)

当一个路由的 handlers 全部用完,会调用 src/tea.cj 的next()

func next(ctx: TeaContext): Unit { // 1. 按 method + 前缀树取出候选路由列表 // 2. 逐个尝试 matchPath 匹配 // 3. 命中后:ctx.indexHandler = 0,执行 handlers[0] // 4. 都没命中:抛出 404 }

为什么中间件一定在 Handler 之前?

答案在注册逻辑 src/tea.cj:

let handlers = ArrayList<Handler>() if (middleware.size > 0) { handlers.add(all: middleware) // ① 先放中间件 } if (handler.isSome()) { handlers.add(handler.getOrThrow()) // ② 再放主 Handler }

📌中间件永远排在主 Handler 前面——这正是"拦截"二字的由来:它们先于业务逻辑执行,也先有机会"截停"请求。


四、在中间件之间传递数据:set 与 get 共享参数 🔗

同一条拦截链上的所有函数共享同一个TeaContext。想把"上游算出来的东西"交给"下游",用set/get即可(实现在 src/context.cj,底层是一个ConcurrentHashMap)。

app.use { c => c.set("userId", "u-10086") // 上游:写入共享参数 c.next() } app.get("/test") { c => let uid = c.get("userId") // 下游:读取 c.sendString("hello ${uid}") // => "hello u-10086" }

典型用途:认证中间件把登录用户信息set进上下文,后续所有 Handler 直接get取用,避免重复解析 Token。

🧠 注意:set的 value 是Any类型,取出后通常需要as转型(如book as String)。


五、实战:用内置 CORS 中间件拦截跨域请求 🛡️

Tea 自带一个非常完整的CORS 跨域中间件,位于 src/middleware/cors/cors.cj。它是"中间件能力"的最佳样板——一个函数,搞定预检请求(OPTIONS)来源白名单凭证控制

它拦截了什么?

  • 读取请求头Origin,比对白名单allowOrigins(支持*://*.通配子域)。
  • 遇到OPTIONS预检 → 直接返回204 No Content不调用c.next(),把跨域请求"就地拦截"。
  • 普通请求 → 写入Access-Control-Allow-*系列响应头后c.next()放行。

核心拦截片段(src/middleware/cors/cors.cj):

// OPTIONS 预检且无方法头 → 直接放行给后续 if (c.getMethod() == MethodOptions && c.getRequestHeader(HeaderAccessControlRequestMethod) == "") { c.vary(HeaderOrigin) return c.next() } // ... 校验来源、设置响应头 ... return c.sendStatus(StatusNoContent) // 拦截:不进入业务

怎么用?只需 3 行

import tea.middleware.cors.cors let app = Tea.default() app.use(cors(CorsConfig(allowOrigins: ["https://example.com"]))) app.get("/") { c => c.sendString("ok") } app.run(8080)

✅ 这让你零成本获得生产级跨域拦截——来源校验、预检响应、Credentials互斥校验(*与凭证不可并存会直接抛错)全部内置。


六、进阶:中间件 vs 钩子函数 Hooks 的区别 ⚙️

Tea 除了"请求期"的中间件,还提供 6 类生命周期钩子(Hooks),定义在 src/hooks.cj。它们的作用时机完全不同:

维度中间件(use / next)钩子函数(Hooks)
触发时机每个 HTTP 请求框架生命周期节点
典型用途鉴权、日志、CORS、限流路由/分组注册监听、启动、关闭
能否拦截请求✅ 能(不调 next 即拦截)❌ 否(只做旁路通知)

6 类钩子一览:

  • OnRoute/OnName—— 添加 / 命名路由之后
  • OnGroup/OnGroupName—— 添加 / 命名分组之后
  • OnListen—— 启动监听之前
  • OnShutdown—— 调用shutDown()
app.hooks.registerOnListen { println("即将启动,可在此做预热 / 校验") }

🎯 选型口诀:要拦截请求 → 用中间件;要监听框架事件 → 用 Hooks。


七、错误兜底:中间件抛异常怎么办?🩹

拦截链中任何函数抛出异常都不会让服务崩掉——Tea 在请求入口处统一捕获,并交给错误处理器(src/tea.cj、src/tea.cj):

try { next(ctx) } catch (e: Exception) { // 有错误没被处理 → 调用 errorHandler if (!ctx.app.handlerError(ctx, e)) { throw e // 处理器返回 false 才继续抛 } }
  • 默认defaultErrorHandler会读取Error中的状态码,回写5xx响应体并返回true(表示已兜底)。
  • 你可通过TeaConfig.errorHandler自定义,比如统一返回 JSON 错误结构、打日志、埋点。

🧩 配合中间件,你可以在最外层写一个"异常日志中间件",再交给errorHandler统一格式化响应,形成"记录 → 兜底"的完整闭环。


八、快速上手清单(5 步打造拦截链)✅

  1. 构造应用let app = Tea.default()(src/tea.cj)。
  2. 挂全局中间件app.use { c => ... ; c.next() }做统一日志。
  3. 按前缀加固app.use("/admin") { ... }做鉴权拦截。
  4. 组内共享:用c.set / c.get在拦截链中传递用户态。
  5. 接入 CORSapp.use(cors(CorsConfig(...)))一键跨域。

📖 更多 API 细节(路由参数、配置项、常量)可查阅 README.md;完整示例见 src/test/tea_test.cj 与 src/test/cors_test.cj;架构设计说明见 doc/design.md。


写在最后

Tea 的中间件机制,把"洋葱模型"做得极其克制:一个next()贯穿两级推进,一个set/get完成数据流转,一个use覆盖全局 / 前缀 / 分组三种作用域。理解这三点后,你就能为仓颉 Web 框架搭建出既灵活又安全、层层可控的请求拦截链。🚀

【免费下载链接】tea仓颉语言轻量级的、函数式的、高效的HTTP Web后端框架项目地址: https://gitcode.com/Cangjie-SIG/tea

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询