Express.js登出路由无法重定向/渲染的原因及修复方案
Express.js 登出路由失效问题
我的Express.js项目中登出路由失效,即使切换至其他路由也无法完成渲染或重定向,但代码中的console.log("am clicked");能够正常执行。
相关代码:
const express = require('express') const app = express() const path = require('path') const fs = require('fs') const port = 7000 const sqlite3 = require("sqlite3").verbose(); const session = require('express-session'); const cookieParser = require('cookie-parser'); const bodyparser = require('body-parser'); let fdata = require('./fashion.json'); fdata = fdata.Sheet1; app.use(session({ secret: 'keyboardwarrior', resave: false, saveUninitialized: true, cookie: { maxAge: 30 * 60 * 1000 // 30 minutes } })); app.set("view engine", "ejs"); app.set("views", path.join(__dirname, "views")); app.use(express.static(path.join(__dirname, "public"))); app.use(express.urlencoded({ extended: false })); app.use(bodyparser.json()); app.use(cookieParser()); app.get('/', (req, res) => { res.render('shop'); }) app.get('/shop', (req, res) => { res.render('shop'); }) app.get('/checkout', (req, res) => { res.render('checkout') }) app.get('/fashion', (req, res) => { res.send(fdata) }) // Loggedin app.get('/loggedin', (req, res) => { res.render("shop") }) // Blank app.get('/blank', (req, res) => { res.render("blank") }) // Register app.post('/register', async (req, res) => { const {firstname, lastname, usermail, password} = req.body; if (!usermail || !password) return res.status(400).send({error: "Email or password missing."}); db.serialize(() => { db.each('SELECT COUNT(*) as count FROM user WHERE email = ?', [usermail], (err, row) => { console.log(row) if(err) { console.log(err.message); } else if(row.count > 0) { console.log('there is a match'); } else { db.run('INSERT INTO user (fname, lname, email, pass) VALUES(?,?,?,?)', [firstname, lastname, usermail, password], (err) => { if (err) { return console.log(err.message); } console.log("User successfully registered."); }) res.redirect('/') } }); }); }) // const isLoggedIn = (req, res, next) => { // if(req.session.currentUser){ // next(); // }else{ // res.redirect("/"); // } // }; // app.get("/", isLoggedIn, (req, res) => { // res.render("/", {currentUser: req.session.currentUser}); // }); app.get('/user/:id', function(req,res){ const id = req.params.id; db.serialize(()=>{ db.each('SELECT * FROM user WHERE _id = ?', [id], (err, row) => { try { console.log(`${row._id}`); res.send(row); }catch (err) { res.status(400).send(err); } }) }) }); // login app.post('/', async (req, res) => { const {usermail, password} = req.body; db.serialize(()=>{ db.each('SELECT _id ID, email USEREMAIL, pass USERPASS FROM user WHERE email = ?', [usermail], (err, row) => { if(err){ return console.error(err.message); }else { try { if(row.USEREMAIL && row.USEREMAIL === usermail) { console.log(`${row.ID}`); req.session.isLoggedIn = true; req.session.currentUser = `${row.USEREMAIL}`; res.render('shop', {uid: `${row.ID}`}); } } catch (err) { res.status(400).send(err); } } }) }) }) app.post('/logout', (req, res) => { console.log("am clicked"); if (req.session) { req.session.destroy(); console.log('have been clicked'); } return res.render('shop', {uid: ``}); }); const db = new sqlite3.Database('users.db', err => { if (err) { return console.error(err.message); } console.log("Successful connection to the database 'users.db'"); }); app.all('*', (req, res) => { res.render('404'); }) app.listen(port, () => { console.log(`Listening on port ${port}`) })
预期效果:执行登出操作后重新渲染首页路由。
问题分析与修复方案
核心问题
req.session.destroy()是异步操作:直接在调用它后执行res.render,会话还未完全销毁,后续渲染可能仍携带旧会话信息。- 直接渲染而非重定向:
res.render不会触发浏览器路由刷新,前端可能保留登录状态DOM,导致视觉上无变化。
修复后的登出路由代码
app.post('/logout', (req, res) => { console.log("am clicked"); if (req.session) { // 等待会话销毁完成后再执行重定向 req.session.destroy((err) => { if (err) { console.error('销毁会话失败:', err); return res.status(500).send('登出失败'); } console.log('have been clicked'); // 重定向到首页,强制浏览器刷新页面 res.redirect('/'); }); } else { // 无会话直接重定向 res.redirect('/'); } });
额外优化建议
- 合并重复路由:
/和/shop逻辑一致,可合并减少冗余:app.get(['/', '/shop'], (req, res) => { // 传递用户状态给模板,方便前端控制登录/登出按钮展示 res.render('shop', { uid: req.session.currentUser ? req.session.currentUser.id : '' }); }); - 完善登录逻辑:补充用户不存在的判断,避免报错:
app.post('/', async (req, res) => { const { usermail, password } = req.body; db.serialize(() => { db.get('SELECT _id ID, email USEREMAIL, pass USERPASS FROM user WHERE email = ?', [usermail], (err, row) => { if (err) { return console.error(err.message); } // 判断用户是否存在 if (!row) { return res.status(401).send('邮箱或密码错误'); } // 这里建议用bcrypt处理密码,不要明文存储/比较 if (row.USEREMAIL === usermail && row.USERPASS === password) { req.session.isLoggedIn = true; req.session.currentUser = { email: row.USEREMAIL, id: row.ID }; res.redirect('/'); } else { res.status(401).send('邮箱或密码错误'); } }) }) }) - 密码安全优化:当前明文存储密码风险极高,建议使用
bcrypt对密码哈希后存储,登录时对比哈希值。
内容的提问来源于stack exchange,提问作者wokoma osborn
相关产品推荐
相关产品推荐

