如何在Express中重定向路由并传递订阅价格至支付页面?
问题与解决方案
需求与现状
我有一个订阅类型选择页面,用户选择对应订阅类型后点击「Buy Subscription」按钮,需要获取该订阅类型的价格,并重定向至/payments页面展示该价格。
目前已通过fetch API发送POST请求到Express后端,后端能根据订阅类型(1/6/12)返回对应价格并在控制台打印,但希望不通过URL参数,而是以请求体或其他方式传递价格并完成重定向,询问是否可行。
现有代码
前端代码
// Send a POST request to verify the subscription ID and get its price const purchaseSubscription = async (subscription) => { const response = await fetch(`http://localhost:3000/subscription/purchaseSub/${subscription}`, { method: "POST" }) .then((response) => { if (response.ok) { // Price received back from the backend console.log(response.json()); } }) .catch((err) => { console.log(err); }); }; // Get the value of each subscription type using their dataset omitted...
后端Express路由代码
// Verification of 1, 6 and 12 in getSubscriptionPrice() omitted... subscriptionRouter.post("/purchaseSub/:subType", (req, res) => { const subscriptionType = req.params.subType; if (subscriptionType == 1 || subscriptionType == 6 || subscriptionType == 12) { const price = getSubscriptionPrice(subscriptionType); res.json({price: price}); } else { res.status(400).send(); } });
可行方案
当然可行,以下是几种无需URL参数传递价格的实现方式:
1. 前端存储价格后跳转(最直接)
拿到后端返回的价格后,通过前端存储(比如sessionStorage/localStorage)暂存,再跳转到/payments页面,页面加载时读取存储的价格展示:
修改前端代码:
const purchaseSubscription = async (subscription) => { try { const response = await fetch(`http://localhost:3000/subscription/purchaseSub/${subscription}`, { method: "POST" }); if (response.ok) { const data = await response.json(); // 用sessionStorage暂存价格,关闭浏览器后自动清除 sessionStorage.setItem('subscriptionPrice', data.price); // 跳转到支付页面 window.location.href = '/payments'; } } catch (err) { console.log(err); } };
然后在/payments页面的前端代码中读取:
// 页面加载时获取价格 const price = sessionStorage.getItem('subscriptionPrice'); if (price) { // 渲染价格到页面 document.getElementById('price-display').textContent = `$${price}`; // 可选:用完后清除存储,避免重复使用 sessionStorage.removeItem('subscriptionPrice'); }
2. 后端设置Cookie,前端跳转后读取
后端在返回价格时,将价格存入允许前端读取的Cookie,前端跳转后从Cookie中获取:
修改后端路由代码:
subscriptionRouter.post("/purchaseSub/:subType", (req, res) => { const subscriptionType = req.params.subType; if (subscriptionType == 1 || subscriptionType == 6 || subscriptionType == 12) { const price = getSubscriptionPrice(subscriptionType); // 设置Cookie,maxAge可按需调整,这里设为1小时 res.cookie('subscriptionPrice', price, { maxAge: 3600000, httpOnly: false }); // 返回成功响应,让前端跳转 res.json({success: true}); } else { res.status(400).send(); } });
前端代码修改同方案1,读取方式改为从Cookie获取:
// 在/payments页面读取Cookie const getCookie = (name) => { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); }; const price = getCookie('subscriptionPrice'); if (price) { document.getElementById('price-display').textContent = `$${price}`; // 可选:清除Cookie document.cookie = "subscriptionPrice=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; }
3. 后端生成临时会话,前端跳转后验证(更安全)
如果担心价格被篡改,可以在后端生成临时会话ID,关联价格存储在服务器端(比如Redis、内存缓存),前端跳转时携带会话ID(通过Cookie),支付页面请求后端获取对应价格:
后端修改(示例用内存缓存,生产建议用Redis)
// 内存缓存,生产环境替换为Redis等持久化存储 const sessionCache = new Map(); subscriptionRouter.post("/purchaseSub/:subType", (req, res) => { const subscriptionType = req.params.subType; if (subscriptionType == 1 || subscriptionType == 6 || subscriptionType == 12) { const price = getSubscriptionPrice(subscriptionType); // 生成随机会话ID const sessionId = crypto.randomUUID(); // 存储会话ID和价格,设置过期时间(比如15分钟) sessionCache.set(sessionId, { price, expires: Date.now() + 15*60*1000 }); // 设置HttpOnly Cookie存储会话ID,防止XSS res.cookie('paymentSessionId', sessionId, { maxAge: 15*60*1000, httpOnly: true }); res.json({success: true}); } else { res.status(400).send(); } }); // 支付页面获取价格的接口 subscriptionRouter.get("/payment/price", (req, res) => { const sessionId = req.cookies.paymentSessionId; if (!sessionId) return res.status(400).send('Invalid session'); const sessionData = sessionCache.get(sessionId); if (!sessionData || sessionData.expires < Date.now()) { sessionCache.delete(sessionId); return res.status(400).send('Session expired'); } res.json({price: sessionData.price}); });
前端修改
// 订阅页面跳转逻辑 const purchaseSubscription = async (subscription) => { try { const response = await fetch(`http://localhost:3000/subscription/purchaseSub/${subscription}`, { method: "POST" }); if (response.ok) { window.location.href = '/payments'; } } catch (err) { console.log(err); } }; // 支付页面获取价格 const loadPaymentPrice = async () => { try { const response = await fetch(`http://localhost:3000/subscription/payment/price`, { method: "GET", credentials: 'include' // 携带Cookie }); if (response.ok) { const data = await response.json(); document.getElementById('price-display').textContent = `$${data.price}`; } } catch (err) { console.log(err); } }; // 页面加载时调用 window.addEventListener('load', loadPaymentPrice);
内容的提问来源于stack exchange,提问作者sunflower
相关产品推荐
相关产品推荐

