Egg.js HTTP Controller 装饰器实战指南:声明式路由、参数注入与响应定制

Egg.js HTTP Controller 装饰器实战指南:声明式路由、参数注入与响应定制 后端Web框架【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址https://gitcode.com/gh_mirrors/eg/egg点击查看免费下载导读HTTP Controller 是 Egg.js基于 TypeScript 的声明式编程模型 tegg中用于声明 HTTP 接口的核心机制。通过HTTPController与HTTPMethod装饰器你可以将类 方法直接映射为路径 HTTP 方法无需手写路由表再配合HTTPParam、HTTPQuery、HTTPBody等参数装饰器框架会自动把 HTTP 请求中的各项数据解析并注入到方法参数中。读完本文你将掌握如何声明接口、理解路径优先级规则、熟练使用全部请求参数装饰器并针对默认 JSON 响应之外的场景自定义响应与流式输出。本指南对应的完整文档位于 site/docs/basics/httpcontroller.md示例代码可在 examples/helloworld-tegg/app/port/controller 中查看。使用场景当你需要在应用中提供 HTTP 服务时使用HTTPController装饰器声明 HTTP 接口。它特别适合强依赖 HTTP 协议的场景常见的有SSR 场景在服务端渲染 HTML 并返回给前端SSE 场景通过 Server-Sent Events 与前端实时通信例如实现 AI 对话流式输出依赖 HTTP 协议数据如 Cookie做业务逻辑的场景。从底层实现看控制器会被声明为ControllerType.HTTP见 HTTPController.ts 中ControllerInfoUtil.setControllerType(constructor, ControllerType.HTTP)一行并由 EggHTTPControllerRegistrar.ts 在加载单元创建完成后统一注册进 Egg 的 Router。基本用法声明一个 HTTP 接口使用HTTPController装饰器声明一个类为 HTTP 控制器使用HTTPMethod装饰器声明类中方法对应的具体 HTTP 接口信息。import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam } from egg; HTTPController() export default class SimpleController { // 声明一个 GET /api/hello/:name 接口 HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/hello/:name }) async hello(HTTPParam() name: string) { return { message: hello name, }; } }HTTPController装饰器支持传入path参数来指定控制器的基准 HTTP 路径它会与HTTPMethod中的path参数拼接构成最终的 HTTP 路径。import { HTTPController, HTTPMethod, HTTPMethodEnum } from egg; // 设置 path 参数指定本类所有接口的路径前缀 HTTPController({ path: /api }) export default class PathController { // GET /api/hello HTTPMethod({ method: HTTPMethodEnum.GET, path: hello }) async hello() { // ... } // POST /api/echo HTTPMethod({ method: HTTPMethodEnum.POST, path: echo }) async echo() { // ... } }从源码可以看到HTTPController装饰器除了记录控制器类型与路径外还会通过SingletonProto把该类声明为单例对象见 HTTPController.ts而HTTPMethod装饰器则会把path、method、timeout、priority等信息逐一记录到方法元数据中见 HTTPMethod.ts。接口方法在运行期会被包装为 Egg 中间件框架从容器中取出控制器实例、按元数据解析参数、调用方法并将返回值写入ctx.body核心逻辑见 EggHTTPMethodRegister.ts。路径优先级HTTPMethod装饰器中设置的path使用 path-to-regexp 解析支持简单参数、通配符等特性。当多个HTTPMethod装饰器同时满足路径匹配时需要优先级来决定最终命中的接口优先级越高的接口越先被匹配。Egg 为每个接口自动计算优先级。默认优先级规则可以满足绝大多数场景因此大多数情况下无需手动指定。默认规则如下priority pathHasRegExp ? regexpIndexInPath.reduce((p,c) p c * 1000, 0) : 100000结合具体示例以下接口的默认优先级从低到高排列为PathRegExp indexpriority/*[0]0/hello/:name[1]1000/hello/world/message/:message[3]3000/hello/:name/message/:message[1, 3]4000/hello/world[]100000可以看到路径中包含的正则表达式参数、通配符越多优先级越低完全静态的路径优先级最高100000。对于默认优先级无法满足的业务场景可以通过HTTPMethod装饰器的priority参数手动指定优先级。import { HTTPController, HTTPMethod, HTTPMethodEnum } from egg; HTTPController() export default class PriorityController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /(api|openapi)/echo, priority: 100000, // 为该接口指定更高优先级 }) async high() { // ... } HTTPMethod({ method: HTTPMethodEnum.POST, path: /(api|openapi)/(.), }) async low() { // ... } }请求参数装饰器HTTPHeadersHTTPHeaders装饰器用于获取完整的 HTTP 请求头。:::warning ⚠️ 注意headers 中的 key 会被转换为小写取值时请使用小写字符。 :::import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPHeaders, IncomingHttpHeaders } from egg; HTTPController() export default class ArgsController { // curl http://localhost:7001/api/hello -H X-Custom: custom HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/hello }) async getHeaders(HTTPHeaders() headers: IncomingHttpHeaders) { const custom headers[x-custom]; // ... } }从源码看该装饰器将参数类型标记为HTTPParamType.HEADERS见 HTTPParam.ts运行期由 EggHTTPMethodRegister.ts 直接将ctx.request.headers注入到参数中。HTTPQuery / HTTPQueriesHTTPQuery和HTTPQueries装饰器用于获取 HTTP 请求中的 querystring 参数。HTTPQuery只取第一个参数值类型必须为stringHTTPQueries以数组形式注入一个或多个值类型为string[]。import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPQuery, HTTPQueries } from egg; HTTPController() export default class ArgsController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/query }) async getQueries( // /api/query?userasduserfgh // user asd // users [asd, fgh] HTTPQuery() user?: string, // 不设置 name 时默认使用变量名 HTTPQueries({ name: user }) users?: string[], // 也可以手动指定 name ) { // ... } }这里有一个值得注意的细节HTTPQuery/HTTPQueries/HTTPParam这三个装饰器在未指定name时会通过ObjectUtils.getFunctionArgNameList解析函数参数名自动作为参数名见 HTTPParam.ts 中const name param?.name || argNames[parameterIndex]一行。由于 TypeScript 编译后参数名可能丢失如需保证稳定建议显式传入name。运行期HTTPQuery读取的是ctx.query[name]HTTPQueries读取的是ctx.queries[name]见 EggHTTPMethodRegister.ts。HTTPParamHTTPParam装饰器用于获取 HTTP 请求path中匹配到的路径参数类型只能是string。参数名默认与变量名一致有别名需求时也可手动指定。import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam } from egg; HTTPController() export default class ArgsController { // curl http://127.0.0.1:7001/api/2088000 HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/:id }) async getParamId(HTTPParam() id: string) { // id 的值为 2088000 // ... } // 匹配 path 中第一个被正则捕获的字符 HTTPMethod({ method: HTTPMethodEnum.GET, path: /foo/(.*) }) async getParamBar(HTTPParam({ name: 0 }) bar: string) { // ... } }HTTPBodyHTTPBody装饰器用于获取请求体内容。注入时框架会根据请求头中的content-type解析请求体支持json、text、form-urlencoded其他content-type类型会注入空值。如需获取原始请求体可通过Request装饰器自行处理。import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPBody } from egg; export interface BodyData { foo: string; bar?: number; } HTTPController() export default class ArgsController { // content-type: application/json HTTPMethod({ method: HTTPMethodEnum.POST, path: /api/json-body }) async getJsonBody(HTTPBody() body: BodyData) { // ... } // content-type: text/plain HTTPMethod({ method: HTTPMethodEnum.POST, path: /api/text-body }) async getTextBody(HTTPBody() body: string) { // ... } // content-type: application/x-www-form-urlencoded HTTPMethod({ method: HTTPMethodEnum.POST, path: /api/formdata-body }) async getFormBody( HTTPBody() body: FormData, // 函数应用中为 FormData 类型 // HTTPBody() body: BodyData, // 标准应用中为普通对象 ) { // ... } }运行期HTTPBody注入的是ctx.request.body见 EggHTTPMethodRegister.ts即 Egg 解析后的请求体对象与标准应用中的用法一致。CookiesCookies装饰器用于获取完整的 HTTP Cookies。import { Cookies, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPCookies } from egg; HTTPController() export default class ArgsController { HTTPMethod({ method: HTTPMethodEnum.POST, path: /api/cookies }) async getCookies(HTTPCookies() cookies: Cookies) { return { success: true, cookies: cookies.get(test, { signed: false }), }; } }HTTPRequestHTTPRequest装饰器用于获取完整的 HTTP 请求对象可以获取 url、headers、body 等请求信息具体 API 请参考类型定义。:::warning ⚠️ 注意通过HTTPBody注入请求体后请求体即被消费。如果同时注入HTTPRequest并再次消费请求体会报错注入HTTPRequest但仅获取 url、headers 等、不消费请求体则不受影响。 :::import { HTTPBody, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPRequest } from egg; HTTPController() export default class ArgsController { HTTPMethod({ method: HTTPMethodEnum.POST, path: /api/request }) async getRequest(HTTPRequest() request: Request) { const headerData request.headers.get(x-header-key); const url request.url; // 获取请求体 arrayBuffer const arrayBufferData await request.arrayBuffer(); // ... } HTTPMethod({ method: HTTPMethodEnum.POST, path: /api/request2 }) async getRequest2(HTTPBody() body: object, HTTPRequest() request: Request) { // 同时注入 HTTPBody 和 Request通过 request 读取 header、url 等正常工作 const headerData request.headers.get(x-header-key); const url request.url; // ❌ 错误示例 // 当请求体已通过 HTTPBody 注入后 // 再通过 request 消费请求体会抛出异常 // const arrayBufferData await request.arrayBuffer(); // ... } }这里的Request是标准 Web API 的 Request 对象要求 Node.js 版本 16见 HTTPParam.ts 中assert(nodeMajor 16, ...)的断言。框架在运行期通过 Req.ts 中的initRequest基于ctx.request.href、method、headers 与rawBody构造该对象因此请求体消费是独占性的。该场景的完整示例可参考 examples/helloworld-tegg/app/port/controller/ArgsController.ts其中getRequest与getRequest2展示了这两种注入方式的实际效果。HTTPContext在标准应用中可以使用HTTPContext装饰器获取 Egg 的 Context 对象。:::warning ⚠️ 注意函数应用中不支持HTTPContext装饰器。 :::import { HTTPContext, Context, HTTPController, HTTPMethod, HTTPMethodEnum } from egg; HTTPController() export default class ArgsController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/context }) async getContext(HTTPContext() context: Context) { // ... } }HTTPContext本质是InjectContext的别名见 HTTPParam.ts 末尾的导出运行期框架会把当前请求的ctx直接注入该参数见 EggHTTPMethodRegister.ts 中args[contextIndex!] ctx一行。HTTP 响应默认响应默认情况下当HTTPMethod函数返回对象时框架会使用JSON.stringify处理并设置Content-Type: application/json返回给客户端。import { HTTPController, HTTPMethod, HTTPMethodEnum } from egg; HTTPController() export default class ResponseController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/json }) async defaultResponse() { return { result: hello world, }; } }自定义响应函数应用在函数应用中当需要返回非 JSON 数据或设置 HTTP 响应码、响应头时可以通过全局注入的Response对象设置并返回。import { HTTPController, HTTPMethod, HTTPMethodEnum } from egg; HTTPController() export default class ResponseController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/custom-response }) async customResponse() { // Response 是全局对象无需 import return new Response(h1Hello World/h1, { status: 200, headers: { transfer-encoding: chunked, content-type: text/html; charsetutf-8, x-header-key: from-function, }, }); } }标准应用在标准应用中可以使用 Context 提供的 API 自定义 HTTP 响应码和响应头。import { Context, HTTPContext, HTTPController, HTTPMethod, HTTPMethodEnum } from egg; HTTPController() export default class ResponseController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/custom-response }) async customResponse(HTTPContext() ctx: Context) { // 自定义响应码 ctx.status 200; // 添加自定义响应头 ctx.set(x-custom, custom); // 设置 Content-Type 的语法糖等价于 ctx.set(content-type, application/json) // 支持 json、html 等常见类型见 https://github.com/jshttp/mime-types ctx.type html; return h1Hello World/h1; } }关于标准应用中 Context 的更多能力如ctx.app、ctx.logger、ctx.helper等可阅读 site/docs/basics/objects.md。流式响应只需将流式数据包装为Readable对象并返回即可。import { Readable } from node:stream; import { setTimeout } from node:timers/promises; import { Context, HTTPContext, HTTPController, HTTPMethod, HTTPMethodEnum } from egg; // 构造流式数据 async function* generate(count 5, duration 500) { yield htmlheadtitlehello stream/title/headbody; for (let i 0; i count; i) { yield h2Stream content ${i 1}, ${Date()}/h2; await setTimeout(duration); } yield /body/html; } HTTPController() export default class ResponseController { HTTPMethod({ method: HTTPMethodEnum.GET, path: /api/stream }) async streamResponse(HTTPContext() ctx: Context) { ctx.type html; return Readable.from(generate()); } }流式响应非常适合 SSE 等实时通信场景方法返回Readable对象后框架会将其写入ctx.body由 Koa 逐块输出到客户端从而实现边生成边推送的效果。小结本文围绕 site/docs/basics/httpcontroller.md 的核心内容完整介绍了 Egg.js HTTP Controller 的声明方式、路径拼接与优先级规则、全部请求参数装饰器HTTPHeaders、HTTPQuery/HTTPQueries、HTTPParam、HTTPBody、HTTPCookies、HTTPRequest、HTTPContext以及默认/自定义/流式三类响应写法。这些装饰器的元数据定义集中在 tegg/core/controller-decorator/src/decorator/http/HTTPParam.ts 与 HTTPMethod.ts运行期的参数注入与路由注册实现在 tegg/plugin/controller/src/lib/impl/http 目录下动手实践时可以直接参考 examples/helloworld-tegg/app/port/controller 中的完整示例并结合 examples/helloworld-tegg/test 中的测试用例验证行为。赞分享后端Web框架【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址https://gitcode.com/gh_mirrors/eg/egg点击查看免费下载相关推荐Remix 路由与控制器实战指南从 URL 声明到类型安全响应Remix 路由与控制器实战指南从 URL 声明到类型安全响应 导读 本文是 Remix 全栈框架路由模块的核心参考围绕 .agents/skills/re后端前端Web框架strategic-compact技能详解Everything Claude Code如何建议最佳压缩时机strategic compact技能详解Everything Claude Code如何建议最佳压缩时机 在 Everything Claude CodeAI 插件AI 技能AI 评测Wasp 自定义 HTTP API 端点api 声明完整实战指南路由、认证、中间件与实体注入Wasp 自定义 HTTP API 端点api 声明完整实战指南路由、认证、中间件与实体注入 本篇指南围绕 Wasp当前仓库为 GitHub_TrendWeb框架后端前端CLI开发工具上一篇终极Werkzeug调试器实战指南如何在浏览器中实时调试Python代码错误下一篇彻底搞懂Keras Stateful RNN避免90%的时间序列预测错误创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考