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/变成了一个空对象。 这是为什么呀?跪求高手指定。


4 回复

这个问题涉及到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'));

解释

  1. 序列化中间件:在保存Session之前,我们将Session数据序列化为JSON字符串,以避免Redis无法存储RegExp对象的问题。
  2. 反序列化中间件:在加载Session之后,我们将Session数据反序列化,并尝试将任何字符串形式的RegExp转换回真正的RegExp对象。
  3. 处理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.stringifyRegExp对象转换为字符串,并通过JSON.parse在会话恢复时将其还原为原始对象。这样可以确保RegExp对象能够在会话存储与检索之间正确地保存和恢复。

回到顶部