【问题标题】:response sent twice even after returning and using async await即使在返回并使用异步等待之后也发送了两次响应
【发布时间】:2021-10-12 22:50:11
【问题描述】:

如果验证失败,我试图只向客户端发送一个响应,但该行下方的代码被执行,并且我收到错误“发送后无法设置标头”。为什么我在使用 async/await 时会收到此错误?

exports.register = catchAsync(async (req, res, next) => {
        let toCreate = {}
        mapUsers(toCreate, req.body)
        await validateUser(toCreate, res)
        toCreate.password = await bcrypt.hashPassword(toCreate.password)
        let newUser = await User.create({...toCreate})
        res.status(200).json({
            msg: 'user created', newUser
        })
})

验证用户功能:

module.exports = async (user, res) => {
        if(!user.name) return res.status(400).json({ msg: 'name is required'})
        if(user.name.length < 5) {
            return res.status(400).json({ msg: 'name must have more than 5 characters'})
        }
}

【问题讨论】:

  • 是浏览器的问题吗?发送了额外的 OPTION 请求。

标签: node.js mongoose


【解决方案1】:

当用户名无效时,validateUser 函数会发送响应。无论如何都会创建用户,因为即使用户无效,您也不会停止执行。

您可以从validateUser 返回true 或false,并根据具体情况建立您的响应。 validateUser 也没有 async 部分。因此,为了简单起见,删除 async 并且不再需要等待它:

注册

exports.register = catchAsync(async (req, res, next) => {
        let toCreate = {}
        mapUsers(toCreate, req.body)
        if(validateUser(toCreate, res) === false) {
            return
        }
        toCreate.password = await bcrypt.hashPassword(toCreate.password)
        let newUser = await User.create({...toCreate})
        res.status(200).json({
            msg: 'user created', newUser
        })
})

validateUser

module.exports = (user, res) => {
        if(!user.name) {
            res.status(400).json({ msg: 'name is required'})
            return false
         }
        if(user.name.length < 5) {
            res.status(400).json({ msg: 'name must have more than 5 characters'})
            return false
        }
        return true
}

一个更简洁的解决方案可能是在创建之前验证用户名的单独处理程序。您可以在注册钩子之前在链中添加以下处理程序以进行用户名验证(以及更多)。

   exports.validateUser = (req, res, next) => {
      // only perform the check on the register location. 
      // Maybe add a better check.. This is just for showing the idea
      if(req.url.indexOf('/register') > -1) {
          let toCreate = {}
          mapUsers(toCreate , req.body)
    
          
          // store the mapped users in the request for accessing it later!
          req.auth = req.auth || {} 
          req.auth.mappedUsers = toCreate
          
          if(!user.name) {
            res.status(400).json({ msg: 'name is required'})
            // return for not calling next! .
            return
         }
        if(user.name.length < 5) {
            res.status(400).json({ msg: 'name must have more than 5 characters'})
            // return for not calling next! .
            return
        }
      }
      
      // if it's not register location OR no checks failed, call the next handler!
      next()
   }

创建用户的最终处理程序只会在以下情况下被调用 用户名有效! registerHandler 也不需要是async。 它没有调用async 函数,所以保持简单! . 新的注册处理程序:

exports.register = req, res, next) => {
        // the validateUser handler was called before and already 
        // mapped the user. If the code gets in here, the username is 
        // valid. 
        let toCreate = req.mappedUsers
        toCreate.password = await bcrypt.hashPassword(toCreate.password)
        let newUser = await User.create({...toCreate})
        res.status(200).json({
            msg: 'user created', newUser
        })
}

【讨论】:

    【解决方案2】:

    验证函数中没有任何异步代码,因此使用 await 毫无意义。

    有很多方法可以修复您的代码。一种可能是:

    exports.register = catchAsync(async (req, res, next) => {
        let toCreate = {}
        mapUsers(toCreate, req.body)
    
        // no need to use await since the function is synchronous
        let err = validateUser(toCreate, res)
        if (err)
            return res.status(400).json({ msg: err })
    
        toCreate.password = await bcrypt.hashPassword(toCreate.password)
        let newUser = await User.create({...toCreate})
        res.status(200).json({
            msg: 'user created', newUser
        })
    })
    

    和验证功能

    module.exports = (user) => {
        if(!user.name)
            return 'name is required';
        if(user.name.length < 5)
            return 'name must have more than 5 characters';
    }
    

    【讨论】:

      猜你喜欢
      • 2021-01-06
      • 2020-12-23
      • 2021-08-15
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      • 2020-10-26
      • 2017-11-22
      • 1970-01-01
      相关资源
      最近更新 更多