Estoy escribiendo una API con NodeJS y Express para un proyecto escolar y tengo problemas con lo siguiente:
La función getAuthUserId decodifica el token JWT y obtiene la identificación del usuario en el servidor mongoDB.
Llamo a esta función en una llamada REST "/user/authTest". Pero cuando llamo a esto, el servidor responde antes de que la base de datos pueda devolver la identificación y la variable UId no está definida. Como puede ver, el Id realmente se encuentra. ¿Alguna idea sobre cómo puedo solucionar esto?
El código de llamada API:
apiRoutes.post('/user/authTestID', function(req, res) { var UId = getAuthUserId(req, res); console.log(UId); if (UId) { res.sendStatus(200); }else{ res.sendStatus(400); } });La función:
function getAuthUserId(req, res) { var user = new User(); var token = user.getToken(req.headers); if (token) { var decoded = jwt.decode(token, config.secret); User.findOne({ name: decoded.name }, function(err, user) { if (err) throw err; if (!user) { res.status(403).send({success: false, msg: 'Authentication failed. User not found.'}); return false } else { console.log('Auth for ' + user.name + ' ' + user._id); return user._id } }); } else { res.status(403).send({success: false, msg: 'No token provided.'}); return ''; } }La salida de la terminal:
[nodemon] restarting due to changes... [nodemon] starting `node server.js` Connected to MongoDB undefined ::ffff:192.168.0.111 - POST /user/authTestID HTTP/1.1 400 11 - 175.006 ms Auth for test 58f8954c3602b80552b6f1fb¡Gracias por adelantado!
Necesitas hacer una promesa, como esta.
apiRoutes.post('/user/authTestID', function(req, res) { getAuthUserId(req, res).then(function (UId) => { console.log(UId); if (UId) { res.sendStatus(200); }else{ res.sendStatus(400); } }); }, function(err) { console.log(err.msg) res.status(err.status).send(err.msg); }); function getAuthUserId(req, res) { return new Promise(function(resolve, reject){ var user = new User(); var token = user.getToken(req.headers); if (token) { var decoded = jwt.decode(token, config.secret); User.findOne({ name: decoded.name }, function(err, user) { if (err) throw err; if (!user) { reject({status: 403, msg: 'Authentication failed. User not found.'}); } else { console.log('Auth for ' + user.name + ' ' + user._id); resolve(user._id) } }); } else { reject({status: 403, msg: 'No token provided.'}); } }) }getAuthUserId obtiene el valor en una DEVOLUCIÓN DE LLAMADA! . No puede esperar que devuelva valores de él. Lo más rápido que puede hacer algo como a continuación.
apiRoutes.post('/user/authTestID', function (req, res) { var user = new User(); var token = user.getToken(req.headers); if (token) { var decoded = jwt.decode(token, config.secret); User.findOne({ name: decoded.name }, function (err, user) { if (err) throw err; if (!user) { return res.status(403).send({success: false, msg: 'Authentication failed. User not found.'}); } else { console.log('Auth for ' + user.name + ' ' + user._id); return res.send(user._id) } }); } else { return res.status(403).send({success: false, msg: 'No token provided.'}); // return ''; } });Intente usar la biblioteca Promise como Bluebird
El comentario de James parece un recurso bueno y completo sobre las llamadas asincrónicas. Como han mencionado otros, no puede devolver valores dentro de una devolución de llamada. Puede usar una biblioteca Promise, o puede cambiar su función getAuthUserId para recibir una devolución de llamada y tener su lógica console.log allí:
Ejemplo:
apiRoutes.post('/user/authTestID', function(req, res) { getAuthUserId(req, res, function(UId) { // we're in a your new callback console.log(UId); if (UId) { res.sendStatus(200); }else{ res.sendStatus(400); } }); }); // note new callback param function getAuthUserId(req, res, callback) { var user = new User(); var token = user.getToken(req.headers); if (token) { var decoded = jwt.decode(token, config.secret); User.findOne({ name: decoded.name }, function(err, user) { if (err) throw err; if (!user) { res.status(403).send({success: false, msg: 'Authentication failed. User not found.'}); callback(false) // no more return, call callback with value } else { console.log('Auth for ' + user.name + ' ' + user._id); callback(user._id) // no more return, call callback with value } }); } else { res.status(403).send({success: false, msg: 'No token provided.'}); callback(''); // no more return, call callback with value } }