Nodejs redis-store session 存储 new RegExp对象的问题。
Nodejs redis-store session 存储 new RegExp对象的问题。
我使用redis-store存储session,做了一个用户管理模块,在做用户多条件组合模糊查询时,将上次查询的条件存储在session当中,下次回到列表页面时,保持查询条件。 exports.list = function(req,res,next){ var method = req.method.toLowerCase(); var pageNum = req.param(‘pageNum’); if(!pageNum){ pageNum = 1; } if(method == ‘post’){//用户查询,post请求 var userid = req.body.userid; var username = req.body.username; var phone = req.body.phone; var email = req.body.email; var activeStr = req.body.active; var is_adminStr = req.body.is_admin; var cond = {}; //组合查询条件 if(userid){ cond.userid = new RegExp(userid.trim());//模糊查询,使用正则 } if(username){ cond.username = new RegExp(username.trim());//模糊查询,使用正则 } if(phone){ cond.phone = new RegExp(phone.trim());//模糊查询,使用正则 } if(email){ cond.email = new RegExp(email.trim());//模糊查询,使用正则 } if(activeStr == ‘1’){ cond.active = true; }else if(activeStr == ‘0’){ cond.active = false; } if(is_adminStr == ‘1’){ cond.is_admin = true; }else if(is_adminStr == ‘0’){ cond.is_admin = false; } req.session.userSearchCond = cond; //将查询条件存进session } var searchCond = req.session.userSearchCond; //从session中获取查询条件 console.log(‘session get’,searchCond);//打印查询条件 User.find(searchCond).exec(function(err,totalUsers){ //使用Mongoose,根据条件进行查询 if(err){ return next(err); } var totalPageNum = (totalUsers.length + pageSize -1) / pageSize; User.find(searchCond).sort({create_time: -1}) .skip((pageNum-1)*pageSize).limit(pageSize) .exec(function(err,users){ if(err){ return next(err); } res.render(‘user/user_list’,{ user: req.session.user, users: users, success: req.flash(‘success’).toString(), error: req.flash(‘error’).toString(), totalPageNum: totalPageNum, currentPageNum: pageNum, searchCond : searchCond, title: ‘后台管理-用户列表’ }); }); }); } 当进行post查询时,所有条件能够正常显示, session get { userid: /55/ } 但是再一次get访问user/list路径时,查询条件打印出来为: session get { userid: {} } 查询条件的正则表达式/55/变成了一个空对象。 这是为什么呀?跪求高手指定。
这个问题涉及到Node.js中使用redis-store
存储Session时,如何正确处理new RegExp
对象。Redis默认情况下不支持直接存储JavaScript对象中的RegExp
类型,因为Redis的数据结构并不支持这种类型。
解决方案
1. 使用中间件序列化和反序列化Session数据
我们可以创建一个中间件来序列化和反序列化Session数据,以确保RegExp
对象能够被正确地存储和恢复。
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const app = express();
app.use(session({
store: new RedisStore(),
secret: 'your_secret_key',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 3600000 }, // 1 hour
rolling: true,
}));
// 序列化和反序列化中间件
function serializeSessionMiddleware(req, res, next) {
const originalSave = req.session.save;
req.session.save = async function () {
req.session.data = JSON.parse(JSON.stringify(req.session.data));
await originalSave.call(this);
};
const originalLoad = req.session.load;
req.session.load = async function () {
await originalLoad.call(this);
req.session.data = JSON.parse(JSON.stringify(req.session.data));
// 反序列化RegExp对象
Object.keys(req.session.data).forEach(key => {
if (req.session.data[key] && typeof req.session.data[key] === 'string' && req.session.data[key].startsWith('/')) {
try {
req.session.data[key] = new RegExp(req.session.data[key]);
} catch (e) {
console.error(`Failed to parse RegExp for key ${key}`, e);
}
}
});
};
next();
}
app.use(serializeSessionMiddleware);
// 用户管理模块
app.post('/user/list', (req, res, next) => {
var method = req.method.toLowerCase();
var pageNum = req.param('pageNum');
if (!pageNum) {
pageNum = 1;
}
if (method === 'post') { // 用户查询,post请求
var userid = req.body.userid;
var username = req.body.username;
var phone = req.body.phone;
var email = req.body.email;
var activeStr = req.body.active;
var is_adminStr = req.body.is_admin;
var cond = {}; // 组合查询条件
if (userid) {
cond.userid = new RegExp(userid.trim()); // 模糊查询,使用正则
}
if (username) {
cond.username = new RegExp(username.trim()); // 模糊查询,使用正则
}
if (phone) {
cond.phone = new RegExp(phone.trim()); // 模糊查询,使用正则
}
if (email) {
cond.email = new RegExp(email.trim()); // 模糊查询,使用正则
}
if (activeStr === '1') {
cond.active = true;
} else if (activeStr === '0') {
cond.active = false;
}
if (is_adminStr === '1') {
cond.is_admin = true;
} else if (is_adminStr === '0') {
cond.is_admin = false;
}
req.session.userSearchCond = cond; // 将查询条件存进session
}
var searchCond = req.session.userSearchCond; // 从session中获取查询条件
console.log('session get', searchCond); // 打印查询条件
// 其他逻辑...
});
app.listen(3000, () => console.log('Server started on port 3000'));
解释
- 序列化中间件:在保存Session之前,我们将Session数据序列化为JSON字符串,以避免Redis无法存储
RegExp
对象的问题。 - 反序列化中间件:在加载Session之后,我们将Session数据反序列化,并尝试将任何字符串形式的
RegExp
转换回真正的RegExp
对象。 - 处理
RegExp
对象:在序列化和反序列化过程中,我们特别处理了RegExp
对象,确保它们能够在存储和恢复过程中保持正确。
通过这种方式,可以解决在使用redis-store
存储Session时,new RegExp
对象丢失的问题。
get 的时候,req.body 没东西。req.body 提取的是 post body 里面的东西。
另:楼主之前是写 Java 的吗,看代码有点那意思?
呵呵,见笑了,刚刚开始学习nodejs。 代码中只有Post方法时才重新取值,如果是get方式,直接从session中取值的。 但是get方式从session中取出上回的查询条件时,正则表达式的模糊查询value值不见了。取出的是空对象。
当你在Node.js中使用redis-store
来存储Session时,遇到了将new RegExp
对象存储在Session中,然后再读取时变成空对象的问题。这是因为Redis默认是基于字符串的键值存储,而RegExp
对象不是简单的数据类型,直接存储可能会出现问题。
为了正确地存储和检索包含RegExp
对象的Session数据,你可以使用序列化和反序列化的方法,比如使用JSON.stringify和JSON.parse来处理这些复杂的对象。以下是如何修改你的代码以确保RegExp
对象能够正确地存储和读取:
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const app = express();
app.use(session({
store: new RedisStore({
host: 'localhost',
port: 6379,
// 其他配置...
}),
secret: 'your-secret-key',
saveUninitialized: false,
resave: false,
}));
exports.list = async function(req, res, next) {
let method = req.method.toLowerCase();
let pageNum = req.query.pageNum || 1;
if (method === 'post') {
let userid = req.body.userid;
let username = req.body.username;
let phone = req.body.phone;
let email = req.body.email;
let activeStr = req.body.active;
let is_adminStr = req.body.is_admin;
let cond = {};
if (userid) {
cond.userid = new RegExp(userid.trim(), 'i'); // 模糊查询,使用正则
}
if (username) {
cond.username = new RegExp(username.trim(), 'i'); // 模糊查询,使用正则
}
if (phone) {
cond.phone = new RegExp(phone.trim(), 'i'); // 模糊查询,使用正则
}
if (email) {
cond.email = new RegExp(email.trim(), 'i'); // 模糊查询,使用正则
}
if (activeStr == '1') {
cond.active = true;
} else if (activeStr == '0') {
cond.active = false;
}
if (is_adminStr == '1') {
cond.is_admin = true;
} else if (is_adminStr == '0') {
cond.is_admin = false;
}
// 使用 JSON.stringify 将 RegExp 对象转换为字符串
req.session.userSearchCond = JSON.stringify(cond);
}
let searchCond = req.session.userSearchCond ? JSON.parse(req.session.userSearchCond) : {};
console.log('session get', searchCond); // 打印查询条件
try {
const users = await User.find(searchCond).sort({ create_time: -1 })
.skip((pageNum - 1) * pageSize)
.limit(pageSize)
.exec();
const totalUsers = await User.countDocuments(searchCond).exec();
const totalPageNum = Math.ceil(totalUsers / pageSize);
res.render('user/user_list', {
user: req.session.user,
users: users,
success: req.flash('success').toString(),
error: req.flash('error').toString(),
totalPageNum: totalPageNum,
currentPageNum: pageNum,
searchCond: searchCond,
title: '后台管理-用户列表'
});
} catch (err) {
return next(err);
}
};
在这个示例中,我们通过JSON.stringify
将RegExp
对象转换为字符串,并通过JSON.parse
在会话恢复时将其还原为原始对象。这样可以确保RegExp
对象能够在会话存储与检索之间正确地保存和恢复。