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

Next.js13 App Router迁移报错:Route.ts未导出HTTP方法

解决Next.js 13 App Router + NextAuth迁移报错问题

核心问题分析

你遇到的两个报错根源分别是:

  1. App Router API路由规范不兼容:App Router要求API路由文件必须按HTTP方法导出命名函数(如GET、POST),而非Pages Router的默认导出方式。
  2. NextAuth配置拼写错误:代码里的callback是单数,正确应为callbacks(复数),这个错误会导致JWT和Session回调逻辑完全不执行,进而引发客户端请求的JSON解析错误。

修正后的完整代码

import NextAuth, { type NextAuthOptions, type Session, type JWT } from "next-auth";
import SpotifyProvider from "next-auth/providers/spotify";
import spotifyApi from "@/lib/spotify";
import { LOGIN_URL } from "@/lib/spotify";

// 自定义Token类型
interface CustomToken extends JWT {
  accessToken: string;
  refreshToken: string;
  username: string;
  accessTokenExpires: number;
  error?: string;
}

// 自定义Session类型
interface CustomSession extends Session {
  user: {
    accessToken: string;
    refreshToken: string;
    username: string;
  } & Session["user"];
}

async function refreshAccessToken(token: CustomToken) {
  try {
    spotifyApi.setAccessToken(token.accessToken);
    spotifyApi.setRefreshToken(token.refreshToken);

    const { body: refreshedToken } = await spotifyApi.refreshAccessToken();
    console.log("刷新后的token:", refreshedToken);

    return {
      ...token,
      accessToken: refreshedToken.access_token,
      accessTokenExpires: Date.now() + refreshedToken.expires_in * 1000,
      refreshToken: refreshedToken.refresh_token || token.refreshToken,
    };
  } catch (error) {
    console.error("刷新token失败:", error);
    return {
      ...token,
      error: "RefreshAccessTokenError",
    };
  }
}

const authOptions: NextAuthOptions = {
  providers: [
    SpotifyProvider({
      clientId: process.env.NEXT_PUBLIC_CLIENT_ID!,
      clientSecret: process.env.NEXT_PUBLIC_CLIENT_SECRET!,
      authorization: LOGIN_URL,
    }),
  ],
  secret: process.env.JWT_SECRET,
  pages: {
    signIn: "/login",
  },
  // 修正拼写:callback → callbacks
  callbacks: {
    async jwt({ token, account, user }): Promise<CustomToken> {
      // 首次登录时初始化token
      if (account && user) {
        return {
          ...token,
          accessToken: account.access_token as string,
          refreshToken: account.refresh_token as string,
          username: account.providerAccountId as string,
          accessTokenExpires: (account.expires_at as number) * 1000,
        };
      }

      // token未过期,直接返回
      if (Date.now() < (token as CustomToken).accessTokenExpires) {
        console.log("token有效,无需刷新");
        return token as CustomToken;
      }

      // token过期,执行刷新逻辑
      console.log("token已过期,开始刷新");
      return await refreshAccessToken(token as CustomToken);
    },

    async session({ session, token }): Promise<CustomSession> {
      const customToken = token as CustomToken;
      session.user.accessToken = customToken.accessToken;
      session.user.refreshToken = customToken.refreshToken;
      session.user.username = customToken.username;

      return session as CustomSession;
    },
  },
};

// App Router要求导出HTTP方法对应的处理函数
export const GET = NextAuth(authOptions);
export const POST = NextAuth(authOptions);

关键修正点说明

  • API路由导出方式:替换默认导出为export const GET = NextAuth(authOptions)和export const POST = NextAuth(authOptions),符合App Router的路由规范。
  • 修复拼写错误:将callback改为callbacks,确保JWT和Session回调逻辑正常执行。
  • TypeScript类型完善:定义CustomToken和CustomSession接口,替换any类型,提升代码类型安全性。
  • 环境变量处理:使用!断言环境变量已存在(生产环境建议添加更严谨的校验)。
  • 时间戳修正:原代码里account.expires_at + 1000错误,改为account.expires_at * 1000(因为expires_at是秒级时间戳,需要转为毫秒)。

额外注意事项

  1. 确保@/lib/spotify模块正确配置了Spotify API客户端。
  2. 验证环境变量NEXT_PUBLIC_CLIENT_ID、NEXT_PUBLIC_CLIENT_SECRET、JWT_SECRET已在.env.local中正确设置。
  3. 如果客户端仍有CLIENT_FETCH_ERROR,检查登录页面的NextAuth客户端调用是否符合App Router的使用方式(比如使用useSession钩子时确保包裹在SessionProvider中)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 08:50:58