后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载从 v3.17.0 开始Midway 框架提供了ServerResponse与HttpServerResponse两套响应类实现用于在服务端统一定制成功与失败场景下的通用返回格式解决传统「在ctx上挂ok/fail方法、或分别在中间件与错误过滤器里维护返回逻辑」难以统一维护的问题。读完本文你将掌握在 Koa 场景下使用链式响应对象返回 JSON / Text / Blob / 文件 / 流式数据 / SSE并通过静态模板TPL覆盖与类继承定制返回结构的完整方案。Http 通用响应为什么要统一返回格式在 Koa 场景下一个控制器通常会处理业务逻辑后返回结果过程中必然存在成功与失败两种分支。最常见的两种历史做法是在ctx上扩展ok()、fail()等方法业务代码里try/catch后分别调用例如import { Controller, Get, Inject } from midwayjs/core; import { Context } from midwayjs/koa; Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { try { // ... return this.ctx.ok(/*...*/); } catch (err) { return this.ctx.fail(/*...*/); } } }在 Web 中间件中处理成功返回在错误过滤器中处理失败返回。这两种方式都存在同样的痛点返回逻辑分散在多个位置格式难以全局统一也难以维护。为此框架提供了一套「统一返回」方案——以最常见的返回 JSON 数据为例创建HttpServerResponse实例后调用json()方法链式返回数据。import { Controller, Get, Inject, HttpServerResponse } from midwayjs/core; import { Context } from midwayjs/koa; Controller(/) export class HomeController { Inject() ctx: Context; Get(/success) async home() { return new HttpServerResponse(this.ctx).success().json({ // ... }); } Get(/fail) async home2() { return new HttpServerResponse(this.ctx).fail().json({ // ... }); } }默认情况下HttpServerResponse在成功和失败场景下会提供 JSON 的通用包裹结构。成功场景下接收到的数据如下{ success: true, data: //... }失败场景下接收到的数据如下{ success: false, message: //... }注意json()等数据设置方法必须在链式调用的最后一步调用。从源码实现看这一套行为在 response/base.ts 中定义ServerResponse内部维护一个isSuccess状态位默认为truesuccess()与fail()分别将其置为true/false并返回this以支持链式调用而json()、text()、blob()方法则读取该状态位并调用对应的静态模板。HttpServerResponse继承自ServerResponse在 response/http.ts 中补充了status、header、file、sse、stream等 HTTP 专属能力其中json()会额外写入Content-Type: application/json响应头。两个类的导出入口位于 response/index.ts。常用的响应格式与链式快捷方法HttpServerResponse需要传入当前请求的上下文对象ctx才能实例化const serverResponse new HttpServerResponse(this.ctx);之后以链式形式调用各数据设置方法// json serverResponse.json({ a: 1, }); // text serverResponse.text(abcde); // blob serverResponse.blob(Buffer.from(hello world));除了设置数据的方法还提供了一些快捷方法组合使用例如设置状态码与响应头// status serverResponse.status(200).text(abcde); // header serverResponse.header(Content-Type, text/html).text(divhello/div); // headers serverResponse.headers({ Content-Type: text/plain, Content-Length: 100 }).text(a.repeat(100));从源码看这些快捷方法的实现位于 response/http.tsstatus(code)直接写入this.ctx.res.statusCodeheader(key, value)调用this.ctx.res.setHeader(key, value)headers(headers)优先使用ctx.res.setHeaders批量设置否则逐条调用header()json()/text()会自动写入对应的Content-Typeblob(data, mimeType?)支持第二个参数指定 MIME 类型默认application/octet-stream此外还提供了html(data)text/html与redirect(url, status 302)等未在文档中展开但可直接使用的快捷方法。对应的行为在 test/response/base.test.ts 中有完整断言例如success().json({ a: 1 })会精确返回{ success: true, data: { a: 1 } }而success().blob(Buffer.from(abc))原样返回 Buffer。响应模板全局覆盖与继承定制针对不同的数据设置方法框架提供了不同模板TPL供用户自定义。比如json()方法的默认模板如下源码见 response/base.tsclass ServerResponse { // ... static JSON_TPL (data: Recordany, any, isSuccess: boolean): unknown { if (isSuccess) { return { success: true, data, }; } else { return { success: false, message: data || fail, }; } }; }可以看到失败分支中的message直接取自data如果未传数据则回退为字符串fail。直接覆盖全局模板将全局模板覆盖即可实现自定义HttpServerResponse.JSON_TPL (data, isSuccess) { if (isSuccess) { // ... } else { // ... } };注意直接赋值会修改类级别的静态属性影响所有使用HttpServerResponse的实例。通过继承定制局部模板更推荐的方式是通过继承来定义不同的响应模板这样不会影响全局默认模板class CustomServerResponse extends HttpServerResponse {} CustomServerResponse.JSON_TPL (data, isSuccess) { if (isSuccess) { // ... } else { // ... } };使用时创建实例即可// ... Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { return new CustomServerResponse(this.ctx).success().json({ // ... }); } }继承生效的机制在源码中很关键json()、text()、blob()均通过Object.getPrototypeOf(this).constructor.JSON_TPL(...)获取模板而不是直接引用ServerResponse.JSON_TPL。这意味着只要子类覆写了静态模板实例就会自动使用子类模板无需修改基类。针对text、blob方法的模板同样可以覆盖HttpServerResponse.TEXT_TPL (data, isSuccess) { /*...*/}; HttpServerResponse.BLOB_TPL (data, isSuccess) { /*...*/};需要说明的是从源码看模板回调实际接收三个参数data、isSuccess、ctx文档示例中的两参数写法同样可用第三个参数可用来在模板内访问请求上下文。数据流式响应Stream使用内置的HttpServerResponse的stream()方法处理流式数据返回import { Controller, Get, Inject, sleep, HttpServerResponse } from midwayjs/core; import { Context } from midwayjs/koa; Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { const res new HttpServerResponse(this.ctx).stream(); setTimeout(() { for (let i 0; i 100; i) { await sleep(100); res.send(abc.repeat(100)); } res.end(); }, 1000); return res; } }底层实现位于 response/stream.tsHttpStreamResponse继承自 Node.js 的Transform首次写入数据时自动设置statusCode 200、Transfer-Encoding: chunked与Cache-Control: no-cache并关闭 socket 超时字符串数据直接write非字符串数据会经过JSON.stringify后写出_flush时结束响应。send()写入的数据会先经过STREAM_TPL处理sendError()会记录日志并结束响应。通过STREAM_TPL可以修改数据的返回结构HttpServerResponse.STREAM_TPL (data) { /*...*/};注意这个模板只处理成功的数据且从签名看不接收isSuccess参数。文件流式响应File Download从 v3.17.0 开始可以通过HttpServerResponse简单处理文件下载。传递一个文件路径即可默认使用application/octet-stream响应头返回import { Controller, Get, Inject, sleep, HttpServerResponse } from midwayjs/core; import { Context } from midwayjs/koa; Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { const filePath join(__dirname, ../../package.json); return new HttpServerResponse(this.ctx).file(filePath); } }如需返回不同的类型可通过第二个参数指定 MIME 类型import { Controller, Get, Inject, sleep, HttpServerResponse } from midwayjs/core; import { Context } from midwayjs/koa; Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { const filePath join(__dirname, ../../package.json); return new HttpServerResponse(this.ctx).file(filePath, application/json); } }源码层面file(filePath, mimeType?)的实现response/http.ts会写入Content-Type默认application/octet-stream写入Content-Disposition: attachment; filename文件名其中文件名取自路径的basename从而触发浏览器下载行为通过createReadStream将文件包装为Readable流后交给FILE_TPL处理。通过FILE_TPL可以修改返回结构HttpServerResponse.FILE_TPL (data: Readable, isSuccess: boolean) { /*...*/};SSE 响应Server-Sent Events从 v3.17.0 开始框架提供了内置的 SSEServer-Sent Events支持。SSE 的数据定义如下需要按下面的格式返回接口定义见 interface.tsexport interface ServerSendEventMessage { data?: string | object; event?: string; id?: string; retry?: number; }通过HttpServerResponse定义一个返回实例import { Controller, Get, Inject, sleep, HttpServerResponse } from midwayjs/core; import { Context } from midwayjs/koa; Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { const res new HttpServerResponse(this.ctx).sse(); // ... return res; } }可以通过send和sendEnd进行数据传递const res new HttpServerResponse(this.ctx).sse(); res.send({ data: abcde }); res.sendEnd({ data: end });调用sendEnd后请求将被关闭。也可以通过sendError发送错误const res new HttpServerResponse(this.ctx).sse(); res.sendError(new Error(test error));SSE 的底层行为ServerSendEventStream的实现位于 response/sse.ts是一个继承自Transform的流对象其关键行为包括首次收到数据时自动设置Content-Type: text/event-stream、Cache-Control: no-cache, no-transform、Connection: keep-alive、X-Accel-Buffering: no等 SSE 标准响应头并开启 socket 的setKeepAlive(true)将每条消息按 SSE 协议格式化为event:/retry:/id:/data:行对象类型的data会先JSON.stringify多行文本按\n拆分逐行发送消息之间以空行分隔sendError(error)实际发送一条event: error、data为错误消息的帧sendEnd(message)会为消息补充event: close后发送并关闭请求构造时监听ctx.req的close事件客户端断开时自动结束流handleClose此外还提供了forward()方法可将 OpenAI / Anthropic SDK 等异步迭代流按eventsource、openai、anthropic协议转发为兼容的 SSE 帧openai协议结束时发送[DONE]标记相关选项定义见 interface.ts并在 test/response/http.test.ts 中有基于真实eventsource客户端与 OpenAI / Anthropic SDK 的端到端测试。覆盖 SSE 模板通过SSE_TPL可以修改返回结构import { ServerSendEventMessage } from midwayjs/core; HttpServerResponse.SSE_TPL (data: ServerSendEventMessage) { /*...*/};注意文档中该示例写作FILE_TPL属于笔误正确属性为SSE_TPL见 response/http.ts 源码定义。这个模板只处理成功的数据不会处理sendError的情况且返回也必须是ServerSendEventMessage格式。基础数据响应脱离 Http 场景的 ServerResponse除了 Http 场景之外框架提供了基础的ServerResponse类用于其他非 HTTP场景。ServerResponse包含json、text、blob三种数据返回方法以及success和fail两个设置状态的方法行为和HttpServerResponse一致区别仅在于HttpServerResponse会额外写入 HTTP 响应头。通过继承、覆盖等行为可以非常简单地处理响应值。比如对不同用户做返回区分// src/response/api.ts export class UserServerResponse extends HttpServerResponse {} UserServerResponse.JSON_TPL (data, isSuccess) { if (isSuccess) { return { status: 200, ...data, }; } else { return { status: 500, message: limit exceed }; } }; export class AdminServerResponse extends HttpServerResponse {} AdminServerResponse.JSON_TPL (data, isSuccess) { if (isSuccess) { return { status: 200, router: data.router, ...data }; } else { return { status: 500, message: interal error, ...data }; } };使用返回import { Controller, Get, Inject, sleep, HttpServerResponse } from midwayjs/core; import { Context } from midwayjs/koa; import { UserServerResponse, AdminServerResponse } from ../response/api; Controller(/) export class HomeController { Inject() ctx: Context; Get(/) async home() { // ... if (this.ctx.user xxx) { return new AdminServerResponse(this.ctx).json({ router: /, dbInfo: { // ... }, userInfo: { role: admin, }, status: ok, }); } return new UserServerResponse(this.ctx).json({ status: ok, }); } }这个示例同时展示了两种定制方式的组合UserServerResponse与AdminServerResponse各自继承HttpServerResponse并覆写JSON_TPL互不影响全局默认模板而模板内通过...data展开传入的数据实现了「在不同业务分支返回不同结构」的诉求可复用于控制器、服务层乃至非 HTTP 场景的返回值封装。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Midway 数据响应统一方案ServerResponse 与 HttpServerResponse 实战指南Midway 数据响应统一方案ServerResponse 与 HttpServerResponse 实战指南 导读 从 v3.17.0 开始Midway后端微服务云原生Midway 数据响应Data Response体系详解ServerResponse / HttpServerResponse 统一响应格式实战Midway 数据响应Data Response体系详解ServerResponse / HttpServerResponse 统一响应格式实战 自 v3后端微服务云原生ASP.NET Boilerplate AJAX 实战指南abp.ajax 封装、统一响应模型与错误处理ASP.NET Boilerplate AJAX 实战指南abp.ajax 封装、统一响应模型与错误处理 本文以 ASP.NET BoilerplateAB后端Web框架依赖注入认证鉴权上一篇Telegraf Serializer 插件开发指南基于 EXAMPLE_README 模板编写高质量序列化器文档下一篇Flipper Zero 固件中的 nanopb 安全模型可信数据划分、内存不变量与防御性解码实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考