【问题标题】:Promises vs Async with Jsonwebtokens承诺与异步与 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())的结果相同

标签: javascript asynchronous promise json-web-token


【解决方案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 中学习
        • 对不起,我的英文不是很好,但是代码又短又清晰,只能复制试试。
        猜你喜欢
        • 1970-01-01
        • 2018-10-17
        • 2018-03-02
        • 2021-07-16
        • 2013-03-07
        • 1970-01-01
        • 2019-07-20
        • 2012-12-17
        相关资源
        最近更新 更多