承诺与异步与 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)

这里decodedisVerified有相同的数据体

【讨论】:

【参考方案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的主要内容,如果未能解决你的问题,请参考以下文章

javascript 与异步等待的承诺

使用异步/等待与承诺的区别?

JavaScript NodeJS 如何将流/承诺与异步函数一起使用?

异步 redis 和承诺

异步函数返回承诺,而不是值

异步函数返回承诺,而不是值