Express cookieSession et Mongoose: comment faire de request.session.user un modèle Mongoose?

Quand un nouveau compte utilisateur est créé, je crée newUser, une instance de modèle Mongoose qui ressemble à:

_events: Object errors: undefined isNew: false save: function () { arguments: null caller: null _doc: Object name: 'Joe Smith' ... 

Les données réelles de l’object se trouvent dans la propriété _doc , bien que des accesseurs et des opérateurs existent afin que vous puissiez exécuter:

 user.name = 'Jane Doe' 

et cela fonctionnera bien. Je cours:

 request.session.user = newUser; 

Pour enregistrer l’utilisateur dans la session. Jusqu’ici tout va bien.

Cependant, dans les requêtes suivantes, request.session.user semble n’être que le contenu de _doc. Par exemple:

 name: 'Joe Smith' 

Ce qui est bien, mais cela signifie que je ne peux pas exécuter, par exemple, request.session.user.save () pour enregistrer les modifications.

Je pourrais simplement créer un middleware pour rechercher l’utilisateur associé aux données. Mais j’aimerais en savoir plus sur ce que font Express et Mongoose ici.

Comment faire de request.session.user un modèle Mongoose?

Mise à jour : mon hack middleware actuel:

 // Use as a middleware on routes which need users // TODO: only needed due to request.session.user being saved weirdly var rehydrateUser = function(request, response, next) { if ( request.session.user ) { var hydrated = request.session.user.save if ( ! hydrated ) { console.log('Rehydrating user...'); models.User.findOne({ somefield: request.session.user.somefield }, function (err, user) { if ( err ) { request.session.error = 'User not found in DB!'; request.redirect('/'); } else { request.session.user = user; console.log('Rehydrated user! All repos already being monitored.'); next(); } }) } else { next(); } } else { console.log('No user in session') next(); } } 

La session express est stockée en tant que stockage séparé et a des limites à ce qui peut être stocké ici. Il ne peut pas stocker de données prototypes (méthodes). Regardez-le plutôt comme key<>value stockage.

Les raisons en sont que la session peut être stockée n’importe où (à peu près), telle que: processus en mémoire; bases de données (mongodb); redis; etc.

Pour cette raison, une fois que vous avez mis quelque chose dans l’object de session et que la demande est terminée, Express prendra les données de la session, les parsingra et les collera dans le magasin de session comme un object pur. Par défaut, il s’agit de la mémoire de processus, mais comme mentionné précédemment, il peut s’agir de tout autre stockage.

Ainsi, lors de la prochaine requête http, le middleware de session essaiera de restaurer la session en utilisant un cookie (s’il est utilisé) dans les en-têtes de requête, et demandera au stockage de session de fournir les données en retour. Comme il est déjà formaté – il n’aura pas de contenu .prototype, mais uniquement des données ( object , array , ssortingng , number et autres éléments simples).

Vous devez donc créer à nouveau un modèle à partir de ces données.
Vous pouvez le faire par middleware en express. Donc, dans app.configure juste après la définition de la session vous pouvez faire ceci:

 app.use(function(req, res, next) { if (req.session && req.session.user) { req.session.user = new UserModel(req.session.user); } return next(); }); 

Ceci vérifiera si la session est définie ainsi que l’utilisateur. Et ensuite, nous recréerons le modèle mongoose à partir de ces données.

Comme mentionné dans les commentaires, vous ne devez pas stocker de données utilisateur en session, cela risquerait d’entraîner des incohérences dues à la séparation des responsabilités relatives à la propriété des données. Donc, ne stockez que l’ ID de l’utilisateur, puis utilisez un middleware pour le charger depuis la firebase database.

 app.use(function(req, res, next) { if (req.session && req.session.userID) { UserModel.findById(req.session.userID, function(err, user) { if (!err && user) { req.user = user; next(); } else { next(new Error('Could not restore User from Session.')); } }); } else { next(); } }); 

Après avoir créé le middleware pour vérifier si l’utilisateur est connecté:

 function userRequired(req, res, next) { if (req.user) { next(); } else { next(new Error('Not Logged In'); } } 

Et l’utiliser comme ça:

 app.get('/items', userRequired, function(req, res, next) { // will only come here if user is logged in (req.user !== undefined) }); 

Même si cela fonctionnait (ce qui ne fonctionnera pas, pour les raisons expliquées par Maksims), la persistance de l’instance de modèle utilisateur dans la session serait une mauvaise idée. Si le même utilisateur s’est connecté plus d’une fois, les instances d’utilisateur des deux sessions se désynchroniseraient et continueraient à se remplacer.

Pour ce faire, il est _id de conserver le _id de l’utilisateur dans la session (par exemple, request.session.userId ) et d’installer le middleware Express pour charger l’instance de modèle utilisateur dans request.user afin qu’elle soit disponible pour les autres l’appel en cours. Semblable à ce que vous faites maintenant, mais un peu plus propre.

Regarder un seul object par son _id est incroyablement rapide et parce que vous le faites souvent, il sera déjà en mémoire, de sorte que la surcharge est nulle.

En dépit des problèmes de complexité liés au fait de dépendre de l’object utilisateur entier en fonction de votre magasin de session, il convient de noter que vous pouvez initialiser un object mongoose à partir d’un simple object JS à l’aide de la méthode init() (à partir de cette écriture, v4.4.14).

 req.user = new UserModel().init(req.session.user); req.user.name = 'James'; req.user.save( ... ); 

PAS comme ceci (il va essayer de sauvegarder un nouveau document avec le même _id plutôt que de le mettre à jour):

 req.session.user = new UserModel(req.session.user); req.user.name = 'James'; req.user.save( ... );