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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 09:41:00