Express-session购物车每次请求后session.cart丢失问题求助
问题:Express-session购物车在浏览器请求中始终重置,Postman测试正常
使用Express.js搭配Handlebars开发网站,采用express-session存储购物车信息。遇到如下问题:每次在浏览器中发起添加商品到购物车的请求时,req.session.cart始终为undefined,导致每次都会创建新的购物车,仅能添加一件商品;但在Postman中测试时功能完全正常。
相关代码
cartController.js
const {add, initCart, cartDetail} = require('../../services/cartService') const getCart = async(req, res)=>{ console.log('get cart',req.session) console.log(req.session.cart) console.log(req.session.test) res.json(await cartDetail(req.session.cart)); } const addItemToCart = (req, res)=>{ console.log('Vao add to cart ') console.log(req.body) const {productId} = req.body; req.session.test = 'test'; if(req.session.cart){ console.log('da co cart') } if(!req.session.cart){ req.session.cart = initCart(); } console.log(' before add item to cart',req.session) add(productId, req.session.cart) console.log(req.session.cart) console.log(' after add item to cart',req.session) res.json(req.session.cart) } module.exports = { getCart, addItemToCart }
cartService.js
const {getSpecificProductById}= require('./productService') exports.add = (productId, cart)=>{ const foundProduct = cart.products.find(product=>product.id === productId); if(foundProduct){ foundProduct.quantity = foundProduct.quantity+1; }else{ cart.products.push({id: productId, quantity: 1}); } } exports.cartDetail = async(cart)=>{ const cartDetails = {...cart}; cartDetails.products = await Promise.all(cartDetails.products.map(async product=>{ const productInfo = await getSpecificProductById(product.id); return { ...product, name: productInfo.name, price: productInfo.price, image: productInfo.image, } })) return cartDetails; } exports.initCart = ()=>( { products: [] } )
app.js
const hbs = require('hbs') var indexRouter = require("./routes/home"); var usersRouter = require("./routes/users"); const {aboutRouter} = require("./routes/aboutRouter") const {shoppingCartRouter} = require("./routes/shoppingCartRouter") const {checkoutRouter} = require("./routes/checkoutRouter") const productRouter = require('./routes/productRouter') const authRouter = require("./auth/authRouter"); const profileRouter = require("./routes/profileRouter") const authApiRouter = require('./auth/api') const productApiRouter = require('./routes/APIRoutes/productApiRouter'); const cartApiRouter = require('./routes/APIRoutes/cartApiRouter'); const session = require("express-session"); const passport = require('./auth/passport/index') const MySQLStore = require('connect-mysql')(session) const options = { config:{ host:'localhost', user:'root', password: '280502', database: 'session-db' } } const paginate = require('express-paginate') const cors = require('cors') var app = express(); // view engine setup app.use(cors()) app.set("views", path.join(__dirname, "views")); app.set("view engine", "hbs"); hbs.registerPartials(__dirname + '/views/partials'); hbs.handlebars.registerHelper("minus", (a,b)=>{ return a-b }) var blocks = {}; hbs.handlebars.registerHelper('extend', function(name, context) { var block = blocks[name]; if (!block) { block = blocks[name] = []; } block.push(context.fn(this)); // for older versions of handlebars, use block.push(context(this)); }); hbs.handlebars.registerHelper('block', function(name) { var val = (blocks[name] || []).join('\n'); // clear the block blocks[name] = []; return val; }); app.use(paginate.middleware(5, 20)) app.use(logger("dev")); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, "public"))); app.use(session({ secret: 'very secret keyboard cat', resave: false, saveUninitialized: true, cookie: { httpOnly: false, secure: false, maxAge: 1000 * 60 * 60 * 24 * 3, expires: 1000 * 60 * 60 * 24 * 3, }, store: new MySQLStore(options) })) app.use(passport.authenticate('session')); app.use((req,res,next)=>{ res.locals.user = req.user next(); }); app.use("/", indexRouter); app.use("/users", usersRouter); app.use("/about", aboutRouter); app.use("/products", productRouter) app.use("/api/products", productApiRouter) app.use("/auth", authRouter); //api app.use('/api/auth',authApiRouter); app.use("/cart", shoppingCartRouter) app.use("/api/cart", cartApiRouter); app.use("/checkout", checkoutRouter) app.use("/profile", profileRouter) // catch 404 and forward to error handler app.use(function (req, res, next) { next(createError(404)); }); // error handler app.use(function (err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get("env") === "development" ? err : {}; // render the error page res.status(err.status || 500); res.render("error"); }); module.exports = app;
可能的原因及解决方法
1. 前端请求未携带Session Cookie
Postman会自动保存并发送Cookie,但浏览器在Ajax请求中默认不会携带Cookie,需手动配置:
- 若使用
fetch:fetch('/api/cart/add', { method: 'POST', credentials: 'include', // 跨域时用include,同域可用same-origin headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ productId: 'xxx' }) }) - 若使用Axios:
axios.post('/api/cart/add', { productId: 'xxx' }, { withCredentials: true })
2. Session Cookie配置优化
当前配置中httpOnly: false存在安全风险,且expires与maxAge同时设置可能冲突,建议调整:
cookie: { httpOnly: true, // 禁止前端读取Cookie,提升安全性 secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS时启用 maxAge: 1000 * 60 * 60 * 24 * 3 // 仅保留maxAge即可 }
3. 检查Session存储有效性
确认connect-mysql已正确创建session表,MySQL连接正常。可直接查看数据库中的session表,验证每次请求后session数据是否被正确保存。
4. 调试建议
- 在浏览器开发者工具的
Application标签下,查看是否存在connect.sid(默认session Cookie名),且每次请求都会携带该Cookie; - 在
addItemToCart中打印req.headers.cookie,确认请求是否携带session标识; - 检查MySQL的session表,确认每次请求后session记录是否更新。
内容的提问来源于stack exchange,提问作者Khải Nguyễn
相关产品推荐
相关产品推荐

