NextJS 13 App Router偶现Response body锁定TypeError求助
NextJS 13(App Router)注册时偶发
TypeError: Response body object should not be disturbed or locked问题 问题描述
我用NextJS 13的App Router搭建服务器,新用户注册时偶尔会触发TypeError: Response body object should not be disturbed or locked报错,但并非每次都会出现,无法定位问题根源。当前项目依赖@supabase/auth-helpers-nextjs和next-intl,不确定是否与这两个包相关。只有路由处理器和中间件在处理注册请求与响应,相关报错栈及代码如下:
报错栈
⨯ TypeError: Response body object should not be disturbed or locked at extractBody (node:internal/deps/undici/undici:6433:17) at new Request (node:internal/deps/undici/undici:7314:48) at new NextRequest ([project]/node_modules/next/dist/server/web/spec-extension/request.js:33:14) at NextRequestAdapter.fromNodeNextRequest ([project]/node_modules/next/dist/server/web/spec-extension/adapters/next-request.js:75:16) at NextRequestAdapter.fromBaseNextRequest ([project]/node_modules/next/dist/server/web/spec-extension/adapters/next-request.js:51:35) at doRender ([project]/node_modules/next/dist/server/base-server.js:1215:69) at cacheEntry.responseCache.get.incrementalCache.incrementalCache ([project]/node_modules/next/dist/server/base-server.js:1432:46) at [project]/node_modules/next/dist/server/response-cache/index.js:102:42 at ResponseCache.get ([project]/node_modules/next/dist/server/response-cache/index.js:156:11) at DevServer.renderToResponseWithComponentsImpl ([project]/node_modules/next/dist/server/base-server.js:1360:53)
路由处理器代码(/app/[locale]/auth/register/route.ts)
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'; import { getTranslator } from 'next-intl/server'; import { cookies } from 'next/headers'; import { NextResponse } from 'next/server'; export const dynamic = 'force-dynamic'; export async function POST( request: Request, { params: { locale } }: { params: { locale: string } } ) { const t = await getTranslator(locale, 'auth/register'); const requestUrl = new URL(request.url); const formData = await request.formData(); const email = String(formData.get('email')); const password = String(formData.get('password')); const account_type = String(formData.get('accountType')); const user_name = String(formData.get('userName')); const supabase = createRouteHandlerClient({ cookies }); const { error } = await supabase.auth.signUp({ email, password, options: { emailRedirectTo: `${requestUrl.origin}/auth/callback`, data: { account_type, user_name, }, }, }); if (error) { return NextResponse.redirect( `${requestUrl.origin}/register?error=${error}`, { status: 301, } ); } return NextResponse.redirect( `${requestUrl.origin}/register?message=${t('checkEmail')}`, { status: 301, } ); }
中间件代码(/middleware.ts)
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'; import createMiddleware from 'next-intl/middleware'; import type { NextRequest } from 'next/server'; const intlMiddleware = createMiddleware({ locales: ['en', 'fi'], defaultLocale: 'en', }); export default async function middleware(req: NextRequest) { try { const defaultLocale = req.headers.get('x-default-locale') || 'en'; const res = intlMiddleware(req); res.headers.set('x-default-locale', defaultLocale); const supabase = createMiddlewareClient<Database>({ req, res }); await supabase.auth.getSession(); return res; } catch (error) { console.error(`MIDDLEWARE ERROR`); throw error; } } export const config = { matcher: ['/((?!api|_next|_vercel|.*\..*).*)'], };
问题分析与解决方案
这个错误核心原因是请求/响应对象被重复读取、修改或锁定,结合你的代码和依赖,可从以下几个方向修复:
1. 避免直接操作中间件返回的响应对象
intlMiddleware返回的响应对象可能已经被内部处理过,后续修改头信息并传入Supabase客户端时,可能触发重复操作导致对象锁定。建议创建响应副本再处理:
export default async function middleware(req: NextRequest) { try { const defaultLocale = req.headers.get('x-default-locale') || 'en'; // 创建全新的响应对象,而非直接使用intlMiddleware返回的实例 const res = NextResponse.next(); // 复制intl中间件的响应头到新对象 const intlRes = intlMiddleware(req); intlRes.headers.forEach((value, key) => res.headers.set(key, value)); res.headers.set('x-default-locale', defaultLocale); const supabase = createMiddlewareClient<Database>({ req, res }); await supabase.auth.getSession(); return res; } catch (error) { console.error(`MIDDLEWARE ERROR`); throw error; } }
2. 缓存请求表单数据
虽然代码中只调用了一次request.formData(),但隐性的重复读取(比如依赖内部逻辑)也可能锁定请求体。提前提取并缓存所有字段:
// 路由处理器中提前提取所有表单字段并存储,后续不再操作formData const formData = await request.formData(); const email = String(formData.get('email')); const password = String(formData.get('password')); const account_type = String(formData.get('accountType')); const user_name = String(formData.get('userName'));
3. 升级Supabase Auth Helpers版本
旧版本的@supabase/auth-helpers-nextjs可能存在与NextJS 13 App Router的兼容性问题,导致响应对象被意外锁定。执行升级命令:
npm update @supabase/auth-helpers-nextjs @supabase/supabase-js
4. 调整中间件匹配规则
当前中间件匹配所有非静态资源路径,可能与注册路由的动态配置冲突。排除注册路由:
export const config = { matcher: ['/((?!api|_next|_vercel|.*\..*|auth/register).*)'], };
内容的提问来源于stack exchange,提问作者Julius
相关产品推荐
相关产品推荐

