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

Next.js API始终返回HTML而非JSON,登出功能报错求助

问题:NextAuth v5 + Next.js 14 登出功能实现优化

环境信息

  • NextAuth版本:v5.0.0-beta.4
  • Next.js版本:^14.0.4

当前代码与问题

侧边栏组件(src/components/ap-sidebar.tsx)

import { Button, Nav } from 'react-bootstrap';
import styles from './ap-sidebar.module.css';
import { useRouter } from "next/navigation";

export function Sidebar({ activeTab, setActiveTab }: { activeTab: string, setActiveTab: React.Dispatch<React.SetStateAction<string>>}) {
    const router = useRouter();

    const handleSelect = (selectedTab: string | null, event: React.SyntheticEvent<unknown>) => {
        if (selectedTab) {
            setActiveTab(selectedTab);
        }
    };

    const handleSignOut = async () => {
      try {
        const response = await fetch('/api/signout', { method: 'POST' });
        if (response.status !== 200) {
          console.error("sign out error : " + response.status);
          return;
        }

        const a = await response.json(); // 报错行
        console.log(a.message);

        router.push('/');
      } catch (error) {
        console.error(error);
      }
    };

    return (
    <Nav variant="pills" className={styles.sidebar} activeKey={activeTab} onSelect={handleSelect}>
      <Nav.Item className={styles.navItem}>
        <Nav.Link eventKey="artworks">
            <i className="bi bi-images"></i> &nbsp;
            Artworks
        </Nav.Link>
      </Nav.Item>

      <Nav.Item className={styles.navItem}>
        <Nav.Link eventKey="users">
            <i className="bi bi-people"></i> &nbsp;
            Users
        </Nav.Link>
      </Nav.Item>

      <div className={styles.spaceHolder}></div>
      
      <Nav.Item className={styles.navItem}>
        <Button variant="outline-danger" onClick={handleSignOut}>
            <i className="bi bi-power"></i> Sign Out
        </Button>
      </Nav.Item>
    </Nav>
  );
}

export function ArtworkManager() {
  return <div style={{width: "100%"}}>Artwork Manager</div>;
}

export function UserManager() {
  return <div style={{width: "100%"}}>User Manager</div>;
}

API路由(src/app/api/signout/route.ts)

import { NextApiRequest, NextApiResponse } from 'next';
import { NextRequest, NextResponse } from "next/server";
import { signOut } from '../../../../auth'; 

// export async function POST(req: NextApiRequest, res: NextApiResponse) {
export async function POST(req: NextApiRequest, res: NextApiResponse) {
  await signOut();
  return res.json({ message: 'Signed out' });
  // return res.status(200).json({ message: 'Signed out' });
  // return res.end(JSON.stringify({ message: 'Signed out', status: 200 }));
  // return new Response(JSON.stringify({ message: 'Signed out', status: 200 }), {
  //   headers: { 'Content-Type': 'application/json' },
  // });
}

错误信息

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

尝试过的方案与问题

  • 更新1:改用NextResponse返回JSON,但直接进入catch块,原因未知。
  • 更新2:尝试在组件内直接调用signOut,但报错:
    import { signOut } from '../../auth';
    const handleSignOut = async () => {
      "use server"
      try {
        await signOut({ redirectTo: '/' });
      } catch (error) {
        console.error(error);
      }
    };
    
  • 更新3:通过单独的actions.ts实现Server Actions,可行但希望代码更简洁:
    // actions.ts
    export async function handleSignOutServer() {
      await signOut();
    }
    
    组件调用:
    <Button variant="outline-danger" onClick={() => handleSignOutServer()}>
    

最优解决方案

方案1:组件内直接使用Server Action(最简洁)

无需单独创建actions.ts,直接在组件内定义带"use server"标记的异步函数:

import { signOut } from '../../auth';
// ...

const handleSignOut = async () => {
  "use server";
  await signOut({ redirectTo: '/' });
};

按钮绑定:

<Button variant="outline-danger" onClick={handleSignOut}>
    <i className="bi bi-power"></i> Sign Out
</Button>

说明:Next.js 14中,组件内的异步函数加上"use server"即可成为Server Action,signOut会自动处理会话清除并跳转到指定页面,无需手动调用router.push。

方案2:修复API路由(若坚持使用API路由)

原API路由错误是混用了Pages Router的NextApiRequest/NextApiResponse类型,App Router的API路由应使用正确类型:

import { NextResponse } from "next/server";
import { signOut } from '../../../../auth'; 

export async function POST() {
  await signOut({ redirectTo: '/' });
  return NextResponse.json({ message: 'Signed out' }, { status: 200 });
}

前端调用简化:

const handleSignOut = async () => {
  try {
    await fetch('/api/signout', { method: 'POST' });
  } catch (error) {
    console.error(error);
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:24:55