承诺与异步与 Jsonwebtokens
Posted
技术标签:
【中文标题】承诺与异步与 Jsonwebtokens【英文标题】:Promises vs Async with Jsonwebtokens 【发布时间】:2018-02-16 21:27:58 【问题描述】:我完成了一个 Node 应用教程,然后回去用 async/await 重写代码,以更好地了解它是如何完成的。但是,我有一个路由处理程序,如果不使用 Promise,我就无法正确处理:
getProfile: function(id)
return new Promise(function(resolve, reject)
Profile.findById(id, function(err, profile)
if (err)
reject(err)
return
resolve(profile.summary())
)
)
我改写为:
getProfile: async (req, res, next) =>
const profileId = req.params.id;
const profile = await Profile.findById(profileId);
res.status(200).json(profile)
编辑 2:好的,我也意识到我重写了:
create: function(params)
return new Promise(function(resolve, reject)
var password = params.password
params['password'] = bcrypt.hashSync(password, 10)
Profile.create(params, function(err, profile)
if (err)
reject(err)
return
resolve(profile.summary())
)
)
作为
newProfile: async (params, res, next) =>
const newProfile = new Profile(params);
const password = params.password
params['password'] = bcrypt.hashSync(password, 10)
const profile = await newProfile.save();
return profile.summary()
,
这很可能会导致 jsonwebtokens 出现问题:
我遇到问题的 API 端点使用 jsonwebtokens:
var token = req.session.token
utils.JWT.verify(token, process.env.TOKEN_SECRET)
.then(function(decode)
return controllers.profile.getProfile(decode.id)
)
.then(function(profile)
res.json(
confirmation: 'success',
profile: profile
)
)
.catch(function(err)
res.json(
confirmation: 'fail',
message: 'Invalid Token'
)
)
异步代码适用于对 /profile 路由的 GET 和 POST 请求,但在 API 捕获块中不断收到“无效令牌”消息。我对 Promise 和异步代码都很陌生,所以我确定现在有很多我不理解的地方。
所以我的问题是我如何重写以正确格式传递配置文件对象的承诺?
完整文件:
控制器/ProfileController.js
var Profile = require('../models/Profile')
var Album = require('../models/Album')
var Promise = require('bluebird')
var bcrypt = require('bcryptjs')
module.exports =
index: async (req, res, next) =>
const profiles = await Profile.find();
const summaries = []
profiles.forEach(function(profile)
summaries.push(profile.summary())
)
res.status(200).json(summaries)
,
newProfile: async (params, res, next) =>
const newProfile = new Profile(params);
const password = params.password
params['password'] = bcrypt.hashSync(password, 10)
const profile = await newProfile.save();
return profile.summary()
,
getProfile: function(id)
return new Promise(function(resolve, reject)
Profile.findById(id, function(err, profile)
if (err)
reject(err)
return
resolve(profile.summary())
)
)
,
updateProfile: async (req, res, next) =>
const profileId = req.params;
const newProfile = req.body;
const result = await Profile.findByIdAndUpdate(profileId, newProfile);
res.status(200).json(success: true)
,
getProfileAlbums: async (req, res, next) =>
const profileId = req.params.id;
const profile = await Profile.findById(profileId);
,
newProfileAlbum: async (req, res, next) =>
const newAlbum = new Album(req.body);
console.log('newAlbum', newAlbum)
路由/profile.js:
var express = require('express');
const router = require('express-promise-router')();
const ProfileController = require('../controllers/ProfileController')
router.route('/')
.get(ProfileController.index)
.post(ProfileController.newProfile);
router.route('/:id')
.get(ProfileController.getProfile)
.patch(ProfileController.updateProfile);
router.route('/:id/album')
.get(ProfileController.getProfileAlbums)
.post(ProfileController.newProfileAlbum);
module.exports = router;
路由/account.js:
var express = require('express')
var router = express.Router()
var controllers = require('../controllers')
var bcrypt = require('bcryptjs')
var utils = require('../utils')
router.get('/:action', function(req, res, next)
var action = req.params.action
if (action == 'logout')
req.session.reset()
res.json(
confirmation: 'success'
)
if (action == 'currentuser')
if (req.session == null)
res.json(
confirmation: 'success',
message: 'user not logged in'
)
return
if (req.session.token == null)
res.json(
confirmation: 'success',
message: 'user not logged in'
)
return
var token = req.session.token
utils.JWT.verify(token, process.env.TOKEN_SECRET)
.then(function(decode)
return controllers.profile.getProfile(decode.id)
)
.then(function(profile)
res.json(
confirmation: 'success',
profile: profile
)
)
.catch(function(err)
res.json(
confirmation: 'fail',
message: 'Invalid Token'
)
)
)
router.post('/register', function(req, res, next)
var credentials = req.body
controllers.profile
.newProfile(credentials)
.then(function(profile)
var token = utils.JWT.sign(id: profile.id, process.env.TOKEN_SECRET)
req.session.token = token
res.json(
confirmation: 'success',
profile: profile,
token: token
)
)
.catch(function(err)
res.json(
confirmation: 'fail',
message: err.message || err
)
)
)
router.post('/login', function(req, res, next)
var credentials = req.body
controllers.profile
.find(userName: credentials.userName, true)
.then(function(profiles)
if (profiles.length == 0)
res.json(
confirmation: 'fail',
message: 'Profile not found'
)
return
var profile = profiles[0]
var passwordCorrect = bcrypt.compareSync(credentials.password, profile.password)
if (passwordCorrect == false)
res.json(
confirmation: 'fail',
message: 'Incorrect password'
)
return
var token = utils.JWT.sign(id: profile._id, process.env.TOKEN_SECRET)
req.session.token = token
res.json(
confirmation: 'success',
profile: profile.summary(),
token: token
)
)
.catch(function(err)
res.json(
confirmation: 'fail',
message: err
)
)
)
module.exports = router
utils/JWT.js:
var jwt = require('jsonwebtoken')
var Promise = require('bluebird')
module.exports =
sign: function(obj, secret)
return jwt.sign(obj, secret)
,
verify: function(token, secret)
return new Promise(function(resolve, reject)
jwt.verify(token, secret, function(err, decode)
if (err)
reject(err)
return
resolve(decode)
)
)
【问题讨论】:
你没有在你的重写中返回任何东西 原来没有异步的getProfile
应该写成return Profile.findById(id).then(profile => profile.summary())
, FWIW。
您将希望console.error(err)
错误而不是失败并显示不确定的消息,以便您知道实际问题是什么。
您的重写做了完全不同的事情?!它现在采用不同的参数,对结果做了不同的处理,并假设 Profile.findById
已经返回了一个承诺而不是回调。
@Bergi 抱歉,我最初打错了重写代码。它应该是return profile.summary()
,我希望这与resolve(profile.summary())
的结果相同
【参考方案1】:
Profile.findById
不返回承诺。当它最初工作时,您正在使用回调。将 Profile.findById
包装在一个 Promise 中,然后在包装的函数上使用 await 关键字。
【讨论】:
【参考方案2】:您不应该将其重写为async
函数。这对于Profile.findById
方法的承诺完全没问题:
getProfile: function(id)
return new Promise(function(resolve, reject)
Profile.findById(id, function(err, profile)
if (err) reject(err);
else resolve(profile);
)
).then(profile => profile.summary());
您可以重写 API 端点的代码以使用 async
/await
语法:
async function(req, res, next)
try
const token = req.session.token
const decode = await utils.JWT.verify(token, process.env.TOKEN_SECRET);
const profile = await controllers.profile.getProfile(decode.id);
res.json(
confirmation: 'success',
profile: profile
);
catch(err)
console.error(err);
// maybe also call next(err)?
res.json(
confirmation: 'fail',
message: 'Invalid Token'
);
【讨论】:
【参考方案3】:而不是这个:
jwt.verify(token, config.secret, function (err, decoded)
if (err)
return res.status(500).send(
auth: false,
message: 'Failed to authenticate token.'
);
res.status(200).send(decoded);
);
以async-await
形式使用:
let isVerified = await jwt.verify(token, config.secret)
这里decoded和isVerified有相同的数据体
【讨论】:
【参考方案4】:validate.js:
async verifyToken(jwt,token,key)
if(!token) return ;
return new Promise((resolve,reject) =>
jwt.verify(token,key,(err,decoded) => err ? reject() :
resolve(decoded))
);
auth.js:
const jwt = require('jsonwebtoken');
const verifyToken = require('../functions/validate');
const TOKEN_KEY = "abrakadabra";
const token = await verifyToken(
jwt, req.session.token, TOKEN_KEY
).catch(err => ) || ;
// || - need if decoded is undefined
console.log(token.id);
【讨论】:
答案通常应附有解释,以便人们也可以从您的代码 sn-p 中学习 对不起,我的英文不是很好,但是代码又短又清晰,只能复制试试。以上是关于承诺与异步与 Jsonwebtokens的主要内容,如果未能解决你的问题,请参考以下文章