Koa v3 如何将 Blob、ReadableStream、Response 等 WHATWG 对象作为响应体返回?

发布时间:2026/9/12 11:17:42
Koa v3 如何将 Blob、ReadableStream、Response 等 WHATWG 对象作为响应体返回? Koa v3 如何将 Blob、ReadableStream、Response 等 WHATWG 对象作为响应体返回【免费下载链接】koaExpressive middleware for node.js using ES2017 async functions项目地址: https://gitcode.com/GitHub_Trending/ko/koa如果你从 Koa v2 升级到 v3或者在 Node.js v18 环境中直接开发新服务你手上的响应数据可能不再是字符串或 Buffer而是fetch拿到的Response对象、Web API 生成的Blob或ReadableStream。Koa v3 原生支持把这三类 WHATWG 对象直接赋给ctx.body由框架完成状态码、响应头与请求体的写出。本文基于本仓库的 迁移指南、Response API 文档 和__tests__中的测试用例给出一个可直接运行的示例并说明每类对象在赋值后 Koa 实际做了什么、如何验证响应符合预期。前提条件均来自仓库文档Node.jsv18.0.0 或更高版本这是 Koa v3 的硬性要求见 Readme.md 与 migration-v2-to-v3.md当前仓库 package.json 中engines.node为 18。通过npm install koa安装的 Koa v3仓库当前版本为 3.2.1。一个可运行的示例三种对象各占一个端点迁移文档给出的最小示例是三种对象共用一个中间件migration-v2-to-v3.mdapp.use(async ctx { // Using a Blob ctx.body new Blob([Hello World], { type: text/plain }) // Using a ReadableStream ctx.body new ReadableStream({ start(controller) { controller.enqueue(Hello World) controller.close() } }) // Using a Response object ctx.body new Response(Hello World, { headers: { Content-Type: text/plain } }) })实际使用时更建议按路由拆开每个端点只返回一种对象。下面是完整可运行的示例基于 Readme.md 中 Hello Koa 的启动结构const Koa require(koa); const app new Koa(); // 1. Blob 作为响应体 app.use(ctx { if (ctx.path /blob) { ctx.body new Blob([Hello World], { type: text/plain }); return; } // 2. ReadableStream 作为响应体 // 注意先设置 ctx.type再赋 body // 否则 Content-Type 会默认成 application/octet-stream if (ctx.path /stream) { ctx.type text/plain; ctx.body new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode(Hello )); controller.enqueue(new TextEncoder().encode(World)); controller.close(); } }); return; } // 3. Response 对象作为响应体例如 fetch 的返回值 if (ctx.path /response) { ctx.body new Response(Hello World, { status: 200, headers: { Content-Type: text/plain } }); return; } ctx.body Hello Koa; }); app.listen(3000);启动后访问http://localhost:3000/blob、/stream、/response三个端点即可。Blob、ReadableStream、Response在 Node.js 中是全局构造器无需引入任何模块。赋值后 Koa 实际做了什么这三类对象的赋值逻辑在 lib/response.js 的bodysetter 中最终写出在 lib/application.js 的 respond 流程中。理解这两处行为才能正确控制响应头。Bloblib/response.js#L209-L214如果尚未设置Content-TypeKoa 将其设为bin即application/octet-streamContent-Length自动取blob.size写出时通过Stream.Readable.from(body.stream())转成 Node 流lib/application.js#L320。所以创建 Blob 时带上type如{ type: text/plain }或提前设置ctx.type客户端拿到的才是正确的 MIME 类型而不是默认的application/octet-stream。ReadableStreamlib/response.js#L202-L206仅在未设置Content-Type时默认application/octet-stream不会设置Content-Length长度取决于流式输出写出时经Stream.Readable.from(body)接入Stream.pipeline写入响应lib/application.js#L321-L328。Responselib/response.js#L217-L226Koa 的response.status直接取Response.status遍历Response.headers逐个set到 Koa 响应头如果此时未设置Content-Type会默认application/octet-stream写出时使用body?.body即 Response 的流式 body见 lib/application.js#L322。这个行为让fetch的返回值可以直接透传。以下写法来自测试用例tests/application/respond.test.js#L687-L715app.use(async ctx { const stream new ReadableStream({ start (controller) { controller.enqueue(new TextEncoder().encode(Streaming )); controller.enqueue(new TextEncoder().encode(response )); controller.enqueue(new TextEncoder().encode(from fetch)); controller.close(); } }) const response new Response(stream, { status: 200, headers: { Content-Type: text/plain } }) ctx.body response })同样适用于 JSON 响应体和 Blob 响应体tests/application/respond.test.js#L662-L685、#L717-L737——Response上携带的status和自定义头如X-Custom-Header都会被原样转到客户端。如何验证结果仓库使用 supertest 发起真实请求并断言响应npm test对应 package.json 中的test: node --test可运行全部用例。三个关键断言如下也即你本地验证时应检查的点tests/application/respond.test.js#L514-L594// Blob响应体字节与 Blob 内容一致 const res await request(app.callback()).get(/).expect(200) assert.deepStrictEqual(res.body, Buffer.from(await new Blob([Hello]).arrayBuffer())) // BlobHEAD 请求下响应头content-length 取自 blob.size return request(app.callback()) .head(/) .expect(200) .expect(content-type, application/octet-stream) .expect(content-length, 11) // ReadableStream多个 chunk 拼接为完整响应体 return request(app.callback()) .get(/) .expect(200) .expect(content-type, application/octet-stream) .expect(Buffer.from(Hello World))对你自己的服务可以用curl -i对照检查curl -i http://localhost:3000/blob对照上面的测试断言判断/blob状态码 200响应体为Hello WorldContent-Type为创建 Blob 时指定的text/plain未指定时测试断言的默认值是application/octet-stream/stream状态码 200Content-Type为提前设置的text/plain未设置时为application/octet-stream响应体为两个 chunk 拼接后的Hello World/response状态码与Response构造时的status一致Content-Type取自Response.headers响应体为Hello World。Response端点还可验证状态码透传测试用例中new Response(null, { status: 201, ... })经 Koa 响应后客户端收到 201tests/application/respond.test.js#L619-L631。限制与边界版本docs/api/response.md 中response.body列出的合法类型只有string、Buffer、Stream、Object || Array、null || undefined没有这三类 WHATWG 对象——Blob/ReadableStream/Response支持是 v3 新增从 v2 升级时请参阅 migration-v2-to-v3.md。默认 Content-Type三类对象在未显式设置Content-Type时都会落到application/octet-stream。需要文本、JSON 或其他 MIME 时用ctx.type text/plain须先于ctx.body赋值见示例或在对象上携带正确的头Blob 的type选项、Response 的headers。流式写出与错误三类对象最终都转为 Node 可读流经Stream.pipeline(stream, res, ...)写入管道出错时若应用注册了error监听器会回调ctx.onerror(err)lib/application.js#L325-L329。这与 NodeStream类型的 body 走同一条路径Streambody 的onerror与连接关闭时销毁流的行为说明见 docs/api/response.md。文档没有覆盖的写法例如把Request对象作为 body不要自行类推以lib/response.js中bodysetter 实际判断的类型为准。【免费下载链接】koaExpressive middleware for node.js using ES2017 async functions项目地址: https://gitcode.com/GitHub_Trending/ko/koa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考