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
相关产品推荐
相关产品推荐

