Next.js 14: getServerSideProps序列化products时出现undefined值错误
Next.js 14.2.3中getServerSideProps获取Stripe产品的序列化错误解决
问题描述
使用Next.js 14.2.3,在getServerSideProps中通过@stripe/firestore-stripe-payments的getProducts函数从Firestore获取产品列表时,触发序列化错误:
Server Error
Error: Error serializing.productsreturned fromgetServerSidePropsin "/".
Reason:undefinedcannot be serialized as JSON. Please usenullor omit this value.
相关代码
import Head from "next/head"; import Header from "@/components/Header"; import Banner from "@/components/Banner"; import { Movie } from "@/typings"; import requests from "@/utils/requests"; import Row from "@/components/Row"; import useAuth from "@/hooks/useAuth"; import { useRecoilValue } from "recoil"; import { modalState } from "@/atoms/moduleAtom"; import Modal from "@/components/Modal"; import Plans from "@/components/Plans"; import { Product, getProducts } from "@stripe/firestore-stripe-payments"; import payments from "@/lib/stripe"; interface Props { netflixOriginals: Movie[]; trendingNow: Movie[]; topRated: Movie[]; actionMovies: Movie[]; comedyMovies: Movie[]; horrorMovies: Movie[]; romanceMovies: Movie[]; documentaries: Movie[]; products: Product[]; } const Home = ({ netflixOriginals, actionMovies, comedyMovies, documentaries, horrorMovies, romanceMovies, topRated, trendingNow, products, }: Props) => { console.log(products); const { logout, loading } = useAuth(); const showModal = useRecoilValue(modalState); const subscription = false; if (loading || subscription === null) return null; if (!subscription) return <Plans />; return ( <div // 打开模态框时禁止滚动 className={`relative h-screen bg-gradient-to-b lg:h-[140vh] ${showModal && "!h-screen overflow-hidden"}`} > <Head> <title>Home - Netflix</title> <link rel="icon" href="/favicon.ico" /> </Head> <Header /> <main className="relative pl-4 pb-24 lg:space-y-24 lg:pl-16"> <Banner netflixOriginals={netflixOriginals} /> <section className="md:space-y-24"> <Row title="Trending Now" movies={trendingNow} /> <Row title="Top Rated" movies={topRated} /> <Row title="Action Thrillers" movies={actionMovies} /> {/* My List Component */} <Row title="Comedies" movies={comedyMovies} /> <Row title="Scary Movies" movies={horrorMovies} /> <Row title="Romance Movies" movies={romanceMovies} /> <Row title="Documentaries" movies={documentaries} /> </section> </main> {showModal && <Modal />} </div> ); }; export const getServerSideProps = async () => { const products = await getProducts(payments, { includePrices: true, activeOnly: true, }).then((res) => res).catch((error) => console.log(error.message)); const [ netflixOriginals, trendingNow, topRated, actionMovies, comedyMovies, horrorMovies, romanceMovies, documentaries, ] = await Promise.all([ fetch(requests.fetchNetflixOriginals).then((res) => res.json()), fetch(requests.fetchTrending).then((res) => res.json()), fetch(requests.fetchTopRated).then((res) => res.json()), fetch(requests.fetchActionMovies).then((res) => res.json()), fetch(requests.fetchComedyMovies).then((res) => res.json()), fetch(requests.fetchHorrorMovies).then((res) => res.json()), fetch(requests.fetchRomanceMovies).then((res) => res.json()), fetch(requests.fetchDocumentaries).then((res) => res.json()), ]); return { props: { netflixOriginals: netflixOriginals.results, trendingNow: trendingNow.results, topRated: topRated.results, actionMovies: actionMovies.results, comedyMovies: comedyMovies.results, horrorMovies: horrorMovies.results, romanceMovies: romanceMovies.results, documentaries: documentaries.results, products, }, }; }; export default Home;
已尝试的解决方法
- 为
getProducts调用添加错误处理以记录问题 - 通过
JSON.stringify和JSON.parse将products数组中的undefined值转为null - 多次重启Firebase、创建新数据库并设置新的Stripe产品
- 验证Firebase数据库中产品集合存在
- 确认Firebase已正确关联应用(注册用户可成功添加到数据库)
解决方案
1. 处理getProducts的错误返回,避免undefined
当前catch块仅打印错误但返回undefined,导致products变量为undefined,直接序列化会报错。修改错误处理逻辑,返回空数组:
const products = await getProducts(payments, { includePrices: true, activeOnly: true, }).catch((error) => { console.log(error.message); return []; // 错误时返回空数组,避免undefined });
2. 递归清理Product对象中的深层undefined值
getProducts返回的Product对象可能存在深层的undefined属性,JSON.stringify/parse无法完全处理嵌套结构,需要手动递归清理:
// 递归将对象中的undefined转为null const cleanUndefined = (obj) => { if (Array.isArray(obj)) { return obj.map(item => cleanUndefined(item)); } if (obj && typeof obj === 'object') { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [ key, value === undefined ? null : cleanUndefined(value) ]) ); } return obj === undefined ? null : obj; }; // 在getServerSideProps中使用 const rawProducts = await getProducts(payments, { includePrices: true, activeOnly: true, }).catch((error) => { console.log(error.message); return []; }); const products = cleanUndefined(rawProducts);
3. 确保props返回值的序列化安全性
在返回props时,兜底确保products始终为数组类型,避免意外的undefined:
return { props: { // ...其他props products: products || [], // 确保永远是数组,不会是undefined }, };
最终修改后的getServerSideProps示例
export const getServerSideProps = async () => { const cleanUndefined = (obj) => { if (Array.isArray(obj)) { return obj.map(item => cleanUndefined(item)); } if (obj && typeof obj === 'object') { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [ key, value === undefined ? null : cleanUndefined(value) ]) ); } return obj === undefined ? null : obj; }; const rawProducts = await getProducts(payments, { includePrices: true, activeOnly: true, }).catch((error) => { console.log(error.message); return []; }); const products = cleanUndefined(rawProducts); const [ netflixOriginals, trendingNow, topRated, actionMovies, comedyMovies, horrorMovies, romanceMovies, documentaries, ] = await Promise.all([ fetch(requests.fetchNetflixOriginals).then((res) => res.json()), fetch(requests.fetchTrending).then((res) => res.json()), fetch(requests.fetchTopRated).then((res) => res.json()), fetch(requests.fetchActionMovies).then((res) => res.json()), fetch(requests.fetchComedyMovies).then((res) => res.json()), fetch(requests.fetchHorrorMovies).then((res) => res.json()), fetch(requests.fetchRomanceMovies).then((res) => res.json()), fetch(requests.fetchDocumentaries).then((res) => res.json()), ]); return { props: { netflixOriginals: netflixOriginals.results, trendingNow: trendingNow.results, topRated: topRated.results, actionMovies: actionMovies.results, comedyMovies: comedyMovies.results, horrorMovies: horrorMovies.results, romanceMovies: romanceMovies.results, documentaries: documentaries.results, products, }, }; };
内容的提问来源于stack exchange,提问作者Firato
相关产品推荐
相关产品推荐

