-1

我正在创建一个电子商务网站,将所有数据存储在数据库和会话存储中。对于已登录的用户,只有登录凭据存储在会话存储中。因此,每当网站第一次运行时,应该从我们从会话存储中获得的数据中从数据库中获取数据,但是如果我为此目的使用中间件,则效率不高,因为中间件在每个请求上都运行。那么,有没有办法解决这个问题,或者有没有更好的解决方案更有效?此外,您可能想知道为什么我不直接将数据存储在会话存储中。所以,问题是,当我从会话存储中获取数据时,它不是以猫鼬模型的形式返回的,所以我必须调用数据库一次。我使用 mongo store、express、node 和 ejs。

这是我在索引文件中使用的中间件,用于在登录期间使用存储在会话存储中的 id 从 mongoose 模型中的数据库中获取数据。

app.use(async (req, res, next) => {
    try {
        if(req.session.userid) {
            req.user = await user.findById(userid)
        }
    } catch(err) {
        res.redirect('/' + req.oldUrl + '?err=UserNotFound')
    }
    next()
})

app.use(session({
secret: 'secret',
saveUninitialized: false,
resave: false,
cookie: {
    maxAge: 1000 * 60 * 60 * 24 * 365 * 100,
    secure: false
},
store: store
}))

这是我的猫鼬模型

const mongoose = require('mongoose')
const Schema = mongoose.Schema

const userschema = new Schema({
    Name : {
        type : String,
        required : true
    },
    Email : {
        type : String,
        required : true
    },
    Password : {
        type : String,
        required : true
    },
    IsSeller : {
        type : Boolean,
        defualt : false
    }, 
    Shopname : {
        Type : String,
        default : "-"
    },
    Cart : {
        Items : [
            {
                productId : {
                    type : Schema.Types.ObjectId,
                    ref : 'product',
                    required : true
                },
                quantity : {
                    type : Number, 
                    required : true
                }
            }
        ]
    },
    Myproducts : {
        items : [
            {
                productId : {
                    type : Schema.Types.ObjectId,
                    ref : 'product',
                    required : true
                },
                quantity : {
                    type : Number, 
                    required : true
                }
            }
        ]
    },
    Sdate : {
        type : String,
        default : "-"
    }
})

module.exports = mongoose.model('user', userschema)
4

2 回答 2

0

首先,您可以通过不在每个请求上重新运行中间件来提高中间件的效率。只需将用户保存在会话本身中。据推测,和 user 之间的关系userId永远不会改变,所以每当你第一次获得 时userId,只需获得user并将其保存在会话本身中。然后,您只需在每个会话中查询一次数据库。

app.use(async (req, res, next) => {
    try {
        if(req.session.userid && !req.session.user) {
            req.session.user = await user.findById(userid)
        }
    } catch(err) {
        res.redirect('/' + req.oldUrl + '?err=UserNotFound')
        return;
    }
    next()
});

如果您绝对需要数据req.user而不是req.session.user,您也可以设置中间件:

app.use(async (req, res, next) => {
    try {
        if(req.session.userid && !req.session.user) {
            req.session.user = await user.findById(userid)
        }
    } catch(err) {
        res.redirect('/' + req.oldUrl + '?err=UserNotFound')
        return;
    }
    // populate req.user if user is available
    if (req.session.user) {
        req.user = req.session.user;
    }
    next()
});

如果对象中的某些数据user可能会随着时间而改变,并且您想要一个新的特定信息,那么您可以发出数据库请求以获取这些特定路由中的绝对最新用户对象。这样,您仅在用户最初登录并且路由绝对需要数据库中的最新信息时才访问数据库,而不是在每个请求上。

哦,另一件事。调用后res.redirect(),您需要return从中间件中调用而不是调用next(),因为您已完成该请求的处理并且不希望对该请求进行进一步的路由处理。

于 2021-08-26T22:57:56.077 回答
0

express-sessions如果您不想将其用作通用中间件,则可以直接在其中一个控制器中使用。如果您在登录时设置会话 cookie 并且仅在用户下次登录时检查它,请确保在关闭选项卡或浏览器时用户已注销,或者如果您想保持更灵活并让用户绕过登录表单 x 时间。

// in app.js

const session = require('express-sessions')
const MongoStore = require('connect-mongodb-session')(session)

const store = new MongoStore({
  uri: MONGO_URI
  collection: 'sessions'

app.use(session, { 
  secret: 'myL0ngSecretXXX', 
  resave: false, 
  saveUninitialized: false, 
  store 
})

// in your login controller

exports.setSession = (req, res, next) => {
  req.session.isAuthenticated = true //this saves a session cookie client side
  res.redirect('/dashboard')
}

// Router: 

router.post('/login', loginService)


// Login service

const { setSession } = require('./controllers/login')

// other requires ...

exports.loginService = catchAsync(async (req, res, next) => {
  const allowedFields = ['email', 'password']
  if (!checkFieldsStrict(req.body, allowedFields) || (isEmptyRequest(req.body))) {
    return next(new ErrorResponse(`Invalid request.`, 400))
  }
  const { email, password } = req.body
  if (!email || !password) {
    return next(new ErrorResponse(`Please provide your email and password.`, 400))
  }
  const user = await User.findOne({
    where: { email },
  })
  if (!user) {
    // Don't reveal that the user is unknown:
    return next(new ErrorResponse(`Invalid credentials.`, 401))
  }
  const passwordMatch = await comparePasswords(password, user.password)
  if (!passwordMatch) {
    return next(new ErrorResponse(`Invalid credentials.`, 401))
  }
  setSession()
  // maybe send back a token too in addition to the session?
})

于 2021-08-26T06:39:37.097 回答