You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13中间件无法读取req.body,Edge环境如何转换ReadableStream?

Next.js Edge中间件中Zod验证请求体失败的解决方案

问题背景

在Next.js 13.0.7的Edge中间件中,使用Zod的createUserSchema验证请求体时触发捕获逻辑,原因是req.body返回的是ReadableStream<Uint8Array>,而非预期的可解析JSON对象。

原中间件代码

export default async function middleware(req: NextRequest, res: NextResponse) {
  const { pathname } = req.nextUrl;
  if (pathname.startsWith('/api/user/create')) {
    try {
      createUserSchema.parse({
        body: req.body,
        params: req.nextUrl.searchParams,
      });
      return NextResponse.next();
    } catch (error: any) {
      console.log(req.body);
      return NextResponse.json(
        { success: false, message: error },
        { status: 422, headers: { 'content-type': 'application/json' } }
      );
    }
  }

  return NextResponse.next();
}

控制台输出的req.body内容

<ref *1> ReadableStream {
  _state: 'readable',
  _reader: undefined,
  _storedError: undefined,
  _disturbed: false,
  _readableStreamController: ReadableStreamDefaultController {
  _controlledReadableStream: [Circular *1],
  _queue: S {
  _cursor: 0,
  _size: 0,
  _front: { _elements: [], _next: undefined },
  _back: { _elements: [], _next: undefined }
},
  _queueTotalSize: 0,
  _started: false,
  _closeRequested: false,
  _pullAgain: false,
  _pulling: false,
  _strategySizeAlgorithm: [Function],
  _strategyHWM: 1,
  _pullAlgorithm: [Function],
  _cancelAlgorithm: [Function]
}
}

解决思路与方案

Edge环境不支持Node.js的Buffer模块,需使用标准Web API或Next.js提供的内置方法处理ReadableStream,无需额外polyfill。

推荐方案:使用NextRequest内置的json()方法

NextRequest提供了异步的json()方法,可自动将ReadableStream解析为JSON对象,完全兼容Edge环境。修改后的代码如下:

export default async function middleware(req: NextRequest, res: NextResponse) {
  const { pathname } = req.nextUrl;
  if (pathname.startsWith('/api/user/create')) {
    try {
      // 解析请求体为JSON对象
      const body = await req.json();
      // 传入Zod进行验证
      createUserSchema.parse({
        body,
        params: req.nextUrl.searchParams,
      });
      return NextResponse.next();
    } catch (error: any) {
      console.error('验证失败:', error);
      // 返回Zod的详细错误信息,方便前端调试
      return NextResponse.json(
        { 
          success: false, 
          message: error.issues ? error.issues : error.message 
        },
        { status: 422, headers: { 'content-type': 'application/json' } }
      );
    }
  }

  return NextResponse.next();
}

其他场景处理

  • 如果请求体是文本格式,可使用req.text()获取字符串后再处理
  • 如果是表单数据,可通过new URLSearchParams(await req.text())解析

手动处理ReadableStream(自定义解析)

若需自定义流解析逻辑,可使用标准Web API实现,无需依赖Buffer:

async function streamToJson(stream: ReadableStream): Promise<any> {
  const reader = stream.getReader();
  const chunks: Uint8Array[] = [];
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
  
  // 合并所有Uint8Array块
  const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
  const combinedBuffer = new Uint8Array(totalLength);
  let offset = 0;
  for (const chunk of chunks) {
    combinedBuffer.set(chunk, offset);
    offset += chunk.length;
  }
  
  // 解码为字符串并解析为JSON
  const text = new TextDecoder().decode(combinedBuffer);
  return JSON.parse(text);
}

在中间件中替换req.json()为await streamToJson(req.body)即可。


内容的提问来源于stack exchange,提问作者Stanleyy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 18:31:44