Next.js客户端与服务端组件的Nonce及CORS处理最佳实践
解决方案:Next.js对接WooCommerce Store API的CORS与Nonce管理
一、解决CORS问题:用Next.js API路由做代理
直接从客户端请求WooCommerce域名会触发跨域,最稳妥的方式是通过Next.js的API路由做中转,让客户端请求自己的域名,再由服务端转发请求到WooCommerce,完全规避CORS限制。
1. 创建API代理路由
在app/api/cart/add-item/route.ts中编写代理逻辑:
import { cookies } from 'next/headers'; import { NextResponse } from 'next/server'; export async function POST(request: Request) { const body = await request.json(); const cookieStore = cookies(); const nonce = cookieStore.get('wc_store_api_nonce')?.value; if (!nonce) { return NextResponse.json({ error: 'Nonce缺失' }, { status: 400 }); } // 转发请求到WooCommerce Store API const wcResponse = await fetch('https://my-example-store.local/wp-json/wc/store/v1/cart/add-item', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WC-Store-API-Nonce': nonce, }, body: JSON.stringify(body), }); // 从响应头获取新的Nonce,更新Cookie const newNonce = wcResponse.headers.get('X-WC-Store-API-Nonce'); if (newNonce) { cookieStore.set('wc_store_api_nonce', newNonce, { path: '/', sameSite: 'lax', httpOnly: false, // 允许客户端读取 maxAge: 3600, // 和WooCommerce默认Nonce有效期保持一致 }); } // 返回WooCommerce的响应结果 const data = await wcResponse.json(); return NextResponse.json(data, { status: wcResponse.status }); }
二、Nonce的高效管理
WooCommerce Store API会在所有GET请求的响应头返回X-WC-Store-API-Nonce,我们可以在服务端预渲染时获取并存储到Cookie,实现全局可用,同时自动更新。
1. 在服务端组件中获取并设置Nonce
修改你的Products服务端组件,在获取商品时提取Nonce并写入Cookie:
import { cookies } from 'next/headers'; async function getProducts() { const response = await fetch('https://my-example-store.local/wp-json/wc/store/v1/products'); if (!response.ok) { throw new Error('获取商品数据失败'); } // 提取响应头中的Nonce const nonce = response.headers.get('X-WC-Store-API-Nonce'); if (nonce) { const cookieStore = cookies(); cookieStore.set('wc_store_api_nonce', nonce, { path: '/', sameSite: 'lax', httpOnly: false, maxAge: 3600, }); } return response.json(); } interface Product { id: number, name: string, sku: string, } const Products = async () => { const products: Product[] = await getProducts(); return ( <> {products.map(product => ( <div key={product.sku}> <div>{product.name}</div> {/* 传入商品ID到客户端按钮组件 */} <Button id={product.id} variation="" /> </div> ))} </> ); } export default Products;
2. 客户端组件读取Nonce并调用代理API
修改你的Button客户端组件,调用自己的API路由完成加入购物车操作:
"use client"; async function addToCart({id, variation}: {id: number, variation:string}) { try { const response = await fetch('/api/cart/add-item', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ id, variation_id: variation ? parseInt(variation) : undefined, }), }); if (!response.ok) { const errorData = await response.json(); // 处理Nonce过期:403状态码代表Nonce失效 if (response.status === 403) { alert('会话过期,请刷新页面后重试'); // 可选:自动触发页面刷新获取新Nonce // window.location.reload(); } else { throw new Error(errorData.error || '加入购物车失败'); } } const data = await response.json(); console.log('加入购物车成功:', data); } catch (err) { console.error('加入购物车失败:', err); alert('加入购物车失败,请稍后重试'); } } export default function Button({ id, variation }: { id: number, variation: string }) { const handleEvent = async () => { await addToCart({id, variation}); }; return ( <button onClick={handleEvent}>加入购物车</button> ); }
三、最佳实践总结
- 代理优先:永远通过Next.js API路由中转客户端的修改类请求,既规避CORS,也避免直接暴露WooCommerce域名带来的安全风险。
- Cookie存储Nonce:利用服务端组件的Cookie操作能力,将Nonce全局存储,客户端无需额外状态管理,且能自动更新。
- 过期处理:监听403响应,提示用户刷新页面或自动触发新Nonce的获取,保证流程顺畅。
- 避免HttpOnly:因为客户端需要读取Nonce(即使通过代理,后续扩展场景可能用到),所以Cookie不要设置HttpOnly属性。
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

