msw 中每个接口的 request 是不是只能await 一次?

MSW(Mock Service Worker) 中,每个 request 对象本身并不是只能 await 一次,但你提到的疑问很可能源于对 Response 返回方式流式响应(streaming) 的误解。下面详细解释:


✅ 核心结论

MSW 的 req(Request)对象可以被多次读取或检查(如 req.url, req.method),但 req.body(请求体)作为可读流(ReadableStream),在浏览器中默认只能消费一次。

这其实是 Fetch API 的标准行为不是 MSW 特有的限制


🔍 详细说明

1. Request.body 是一个 ReadableStream

// 在 MSW handler 中
rest.post('/api/login', async (req, res, ctx) => {
  // ❌ 第一次 await req.json() 消费了 body 流
  const body1 = await req.json();

  // ❌ 第二次 await req.json() 会报错!
  // TypeError: Already read
  const body2 = await req.json(); // 💥 报错!

  return res(ctx.json({ ok: true }));
});

这是因为:

  • req 是一个标准的 Request 对象。
  • req.json()req.text()req.formData() 等方法会 读取并关闭底层的 body
  • 流一旦被消费(consumed),就不能再次读取。

⚠️ 这是 浏览器 Fetch API 的设计,Node.js 环境下(如 Vitest + MSW)也模拟了这一行为。


2. 如何“多次使用”请求体?

正确做法:只 await 一次,然后复用变量

rest.post('/api/login', async (req, res, ctx) => {
  const payload = await req.json(); // ✅ 只读一次

  console.log('Payload:', payload);
  if (payload.username === 'admin') {
    return res(ctx.json({ role: 'admin' }));
  }

  return res(ctx.json({ role: 'user' }));
});

如果需要在多个地方处理,提前解析并传参


3. 特殊情况:克隆 Request(不推荐用于 body)

你可以用 req.clone() 克隆整个请求:

const req1 = req.clone();
const req2 = req.clone();

const data1 = await req1.json();
const data2 = await req2.json(); // ✅ 可以!

MSW 官方不建议这样做,因为:

  • 增加内存开销
  • 大多数场景没必要
  • 如果 body 很大(如文件上传),可能影响性能

📌 最佳实践:解析一次,到处使用。


🧪 验证示例(会报错)

// ❌ 错误示范
rest.post('/test', async (req, res, ctx) => {
  console.log(await req.text()); // OK
  console.log(await req.text()); // Uncaught TypeError: Failed to execute 'text' on 'Body': body stream already read
});

📝 总结

问题 答案
MSW 中每个接口的 req 能否多次 await req.json() 不能,因为 req.body 是单次消费的流
这是 MSW 的 bug 吗? 不是,是 Fetch API 标准行为
如何解决? 只解析一次,存到变量中复用
能用 req.clone() 吗? ✅ 技术上可以,但不推荐

如果你正在写 MSW handler 并遇到 “Already read” 错误,检查是否不小心多次调用了 req.json() / req.text() 即可。

posted @ 2026-01-27 20:26  龙陌  阅读(5)  评论(0)    收藏  举报