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

Next.js App Router中如何收发并使用POST请求的表单数据?

Next.js App Router 表单处理与 Pages Router 迁移指南

一、App Router 实现表单提交与数据展示

1. Page1 表单编写(客户端/服务器组件均可)

表单可以直接写在服务器组件中,若需要客户端交互(如实时校验)则添加 'use client' 声明:

// app/page1/page.tsx
'use client'; // 仅需客户端交互时添加

export default function Page1() {
  return (
    <form action="/page2" method="POST">
      <div>
        <label htmlFor="username">用户名:</label>
        <input type="text" id="username" name="username" required />
      </div>
      <div>
        <label htmlFor="email">邮箱:</label>
        <input type="email" id="email" name="email" required />
      </div>
      <button type="submit">提交</button>
    </form>
  );
}

2. Page2 服务器端获取数据并支持客户端渲染

在 App Router 中,通过服务器组件的 POST 函数处理表单提交,再将数据传递给页面,同时嵌套客户端组件实现交互:

第一步:编写 Page2 的服务器组件与 POST 处理函数

// app/page2/page.tsx
import { redirect } from 'next/navigation';
import FormResult from './FormResult';

// 处理POST请求的服务器端逻辑
export async function POST(request: Request) {
  const formData = await request.formData();
  const username = formData.get('username') as string;
  const email = formData.get('email') as string;

  // 数据校验,无效则重定向回page1
  if (!username || !email) {
    redirect('/page1');
  }

  // 重定向到带参数的page2,确保刷新后数据保留
  return Response.redirect(
    new URL(`/page2?username=${encodeURIComponent(username)}&email=${encodeURIComponent(email)}`)
  );
}

// 服务器组件获取URL参数并渲染页面
export default async function Page2({ searchParams }: { searchParams: { username?: string; email?: string } }) {
  const { username, email } = searchParams;

  if (!username || !email) {
    redirect('/page1');
  }

  return (
    <div>
      <h1>提交结果</h1>
      <p>用户名:{username}</p>
      <p>邮箱:{email}</p>
      {/* 嵌套客户端组件实现交互逻辑 */}
      <FormResult username={username} email={email} />
    </div>
  );
}

第二步:编写客户端组件实现交互

// app/page2/FormResult.tsx
'use client';

interface Props {
  username: string;
  email: string;
}

export default function FormResult({ username, email }: Props) {
  const handleCopyEmail = () => {
    navigator.clipboard.writeText(email);
    alert('邮箱已复制');
  };

  return (
    <div style={{ marginTop: '20px' }}>
      <button onClick={handleCopyEmail}>复制邮箱</button>
      <p>客户端交互区:欢迎 {username} 使用本服务</p>
    </div>
  );
}

二、从 Pages Router getServerSideProps 迁移到 App Router

App Router 移除了 getServerSideProps,将其功能拆分到原生服务器组件、路由函数中,以下是核心场景的迁移方案:

1. 基础服务器端数据获取

Pages Router 写法

// pages/user/[id].tsx
export async function getServerSideProps(context) {
  const { id } = context.params;
  const res = await fetch(`https://api.example.com/user/${id}`);
  const user = await res.json();

  return { props: { user } };
}

export default function User({ user }) {
  return <div>用户名:{user.name}</div>;
}

App Router 迁移写法

服务器组件默认在每次请求时执行,直接在组件内编写异步逻辑即可:

// app/user/[id]/page.tsx
export default async function UserPage({ params }: { params: { id: string } }) {
  const { id } = params;
  // 添加 cache: 'no-store' 确保每次请求重新获取,对应getServerSideProps默认行为
  const res = await fetch(`https://api.example.com/user/${id}`, { cache: 'no-store' });
  const user = await res.json();

  return <div>用户名:{user.name}</div>;
}

2. 路由参数与搜索参数获取

  • 动态路由参数:通过组件的 params props 获取
  • URL 搜索参数:通过组件的 searchParams props 获取(如前文 Page2 的例子)

3. 重定向与 404 处理

Pages Router 写法

export async function getServerSideProps(context) {
  const { id } = context.params;
  const res = await fetch(`https://api.example.com/user/${id}`);
  
  if (!res.ok) return { notFound: true };
  if (id === 'admin') return { redirect: { destination: '/dashboard', permanent: false } };
  
  const user = await res.json();
  return { props: { user } };
}

App Router 迁移写法

使用 redirect() 和 notFound() 函数直接在服务器组件中触发:

// app/user/[id]/page.tsx
import { redirect, notFound } from 'next/navigation';

export default async function UserPage({ params }: { params: { id: string } }) {
  const { id } = params;
  const res = await fetch(`https://api.example.com/user/${id}`, { cache: 'no-store' });
  
  if (!res.ok) notFound();
  if (id === 'admin') redirect('/dashboard');
  
  const user = await res.json();
  return <div>用户名:{user.name}</div>;
}

4. 获取请求上下文(Headers/Cookies)

使用 headers() 和 cookies() 函数替代 getServerSideProps 中的 context.req:

// app/profile/page.tsx
import { headers, cookies } from 'next/headers';

export default async function ProfilePage() {
  // 获取请求头
  const userAgent = headers().get('user-agent');
  // 获取Cookie
  const authToken = cookies().get('auth-token')?.value;

  const res = await fetch(`https://api.example.com/profile`, {
    headers: { Authorization: `Bearer ${authToken}` },
    cache: 'no-store',
  });
  const profile = await res.json();

  return (
    <div>
      <p>浏览器:{userAgent}</p>
      <p>用户名:{profile.name}</p>
    </div>
  );
}

5. 客户端组件需要服务器数据

不要在客户端组件中直接请求私有数据,而是通过服务器组件获取后以 props 传递:

// app/page.tsx(服务器组件)
import ClientComponent from './ClientComponent';

export default async function HomePage() {
  const res = await fetch('https://api.example.com/items', { cache: 'no-store' });
  const items = await res.json();
  
  return <ClientComponent items={items} />;
}

// app/ClientComponent.tsx(客户端组件)
'use client';

export default function ClientComponent({ items }) {
  // 这里可实现客户端交互(如筛选、排序)
  return (
    <ul>
      {items.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

内容的提问来源于stack exchange,提问作者Chris J.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 10:25:40