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. 路由参数与搜索参数获取
- 动态路由参数:通过组件的
paramsprops 获取 - URL 搜索参数:通过组件的
searchParamsprops 获取(如前文 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.
相关产品推荐
相关产品推荐

