【问题标题】:Cannot set headers after they are sent to the client with express-validator, express, mongoose and Next.js使用 express-validator、express、mongoose 和 Next.js 将标头发送到客户端后无法设置标头
【发布时间】:2019-12-05 03:45:38
【问题描述】:

我正在使用express-validatormongoosenext.js 中构建登录/注册表单。

听说最好的做法是清理前端后端的数据。

我在前端进行了一些验证(即通过正则表达式检查电子邮件并确保密码具有特定长度)。

但现在我想使用Custom validator 检查我的 mongodb 数据库中是否存在电子邮件。

    .post(body('username').custom(value => {
        UserModel.findOne({ 'email': value }).then(user => {
            if (user) {
                return Promise.reject('E-mail already in use');
            }
        });
    }), 

这是我的其余代码:

var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body } = require('express-validator');

router
    .route('/registration')
    .get(function(req, res) {
        UserModel.find({}, (err, users) => {
            if (err) res.status(500).send(err)
            res.json(users)
        })
    })
    .post(body('username').custom(value => {
        UserModel.findOne({ 'email': value }).then(user => {
            if (user) {
                return Promise.reject('E-mail already in use');
            }
        });
    }), async(req, res, next) => {

        try {
            let newUser = new UserModel(req.body)

            let savedUser = await newUser.save(err => {
                if (err) return res.json({ success: false, error: err })
                return res.json({ success: true })
            })

            if (savedUser) return res.redirect('/users/registration?success=true');
            return next(new Error('Failed to save user for unknown reasons'))

        } catch (err) {
            return next(err)
        }

    })

module.exports = router

这是我得到的错误:

Error: Failed to save user for unknown reasons
    at router.route.get.post (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/users/index.js:34:25)
    at process._tickCallback (internal/process/next_tick.js:68:7)
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)
    at ServerResponse.header (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:767:10)
    at ServerResponse.send (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:267:15)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/index.js:108:17
    at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:71:5)
    at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
    at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:73:5)
    at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
    at Immediate.next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
    at Immediate.<anonymous> (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:635:15)
    at runCallback (timers.js:706:11)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)

当 Mongoose 在设计模型/模式时提供时,我什至需要这个吗?

var mongoose = require('mongoose')
var emailValidator = require('email-validator')
var bcrypt = require('bcrypt') // hashing function dedicated for passwords

const SALT_ROUNDS = 12

var UserSchema = new mongoose.Schema(
  {
    username_email: {
      type: String,
      required: true,
      lowercase: true,
      index: { unique: true }, // I mean this!
      validate: {
        validator: emailValidator.validate,
        message: props => `${props.value} is not a valid email address`
      }
    },
    password: {
      type: String,
      required: true,
      trim: true,
      index: { unique: true },
      minlength: 8
    }
  },
  {
    timestamps: true
  }
)

UserSchema.pre('save', async function preSave(next) {
  var user = this
  var hash
  if (!user.isModified('password')) return next()
  try {
    hash = await bcrypt.hash(user.password, SALT_ROUNDS)
    user.password = hash
    return next()
  } catch (err) {
    return next(err)
  }
})

UserSchema.methods.comparePassword = async function comparePassword(candidate) {
  return bcrypt.compare(candidate, this.password)
};

module.exports = mongoose.model('User', UserSchema)

如果我不这样做是否意味着检查电子邮件是否存在应该移动到前端?如果是这种情况,我将如何处理?

更新

我尝试了尼克的建议,但不知道为什么我仍然得到 ​​p>

`Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

` 这些是更新的路线:

router
    .route('/registration')
    .get(function(req, res) {
        console.log(0)
        UserModel.find({}, (err, users) => {
            console.log(1)
            if (err) res.status(500).send(err)
            console.log(2)
            return res.json(users)
            console.log(3)
        })
    })
    .post(body('email').custom(value => {
        console.log(4)
        UserModel.findOne({ 'email': value }).then(user => {
            console.log(5)
            if (user) {
                console.log(6)
                return Promise.reject('E-mail already in use');
            }
        });
    }), async(req, res, next) => {
        console.log(7)
        try {
            let newUser = new UserModel(req.body)

            let savedUser = await newUser.save(err => {
                if (err) return res.json({ success: false, error: err })
                console.log(8)
                return res.json({ success: true })
            })
            console.log(9)
            if (savedUser) return res.redirect('/users/registration?success=true');
            console.log("savedUser ", savedUser);
            console.log(10)
            return next(new Error('Failed to save user for unknown reasons'))

        } catch (err) {
            return next(err)
        }

    })

Note that pages will be compiled when you first load them.
GET /_next/static/webpack/d691821e71bf01c860e6.hot-update.json 404 299.194 ms - 1862
GET /_next/static/webpack/42c7a9cb77dec12fc8a3.hot-update.json 200 40.276 ms - 35
4
7
9
savedUser  undefined
10
POST /users/registration 200 21.490 ms - 422
Error: Failed to save user for unknown reasons
    at router.route.get.post (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/users/index.js:42:25)
    at process._tickCallback (internal/process/next_tick.js:68:7)
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)
    at ServerResponse.header (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:767:10)
    at ServerResponse.send (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:267:15)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/index.js:108:17
    at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:71:5)
    at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
    at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:73:5)
    at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
    at Immediate.next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
    at Immediate.<anonymous> (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:635:15)
    at runCallback (timers.js:706:11)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)
5
6
(node:68936) UnhandledPromiseRejectionWarning: E-mail already in use
(node:68936) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:68936) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
^C

【问题讨论】:

  • 你能告诉我吗?
  • 更新了我的答案以提供更多细节
  • 在我之前的回答中添加了更新 2。希望这对你现在有用。

标签: node.js express mongoose next.js express-validator


【解决方案1】:

错误“错误 [ERR_HTTP_HEADERS_SENT]:在将标头发送到客户端后无法设置标头”意味着 res.send/json/redirect 在请求期间多次触发。乍一看,这个代码块有可能导致这个错误。

.get(function(req, res) {
        UserModel.find({}, (err, users) => {
            if (err) res.status(500).send(err)
            res.json(users)
        })
    })

在没有查看发出的确切请求的情况下,我建议将 console.log 行放在任何地方,看看您是否可以查明运行时发生的情况。

更新

 .get(function(req, res) {
            UserModel.find({}, (err, users) => {
                // If there's an error, this will fire and continue
                if (err) res.status(500).send(err) 

                // This fires next even if there is an error
                res.json(users)
            })
        })

这可能是导致该问题的原因。有可能会发射两次。解决方法是增加回报。这样可以确保代码不会继续。

 .get(function(req, res) {
            UserModel.find({}, (err, users) => {                    
                if (err) return res.status(500).send(err)
                return res.json(users)
            })
        })

如果这不能解决问题,我建议将 console.log 语句放在所有参数之后,这样你就可以查明它在哪一行失败了。例如:

router
    .route('/registration')
    .get(function(req, res) {
        console.log(0)
        UserModel.find({}, (err, users) => {
          console.log(1)
          if (err) res.status(500).send(err)
          console.log(2)
          res.json(users)
          console.log(3)
        })
    })
    .post(body('username').custom(value => {
        console.log(4)
        UserModel.findOne({ 'email': value }).then(user => {
            console.log(5)
            if (user) {
                console.log(6)
                return Promise.reject('E-mail already in use');
            }
        });
    }), async(req, res, next) => {
        console.log(7)
        try {
            let newUser = new UserModel(req.body)

            let savedUser = await newUser.save(err => {
                if (err) return res.json({ success: false, error: err })
                console.log(8)
                return res.json({ success: true })
            })
            console.log(9)
            if (savedUser) return res.redirect('/users/registration?success=true');
            console.log(10)
            return next(new Error('Failed to save user for unknown reasons'))

        } catch (err) {
            return next(err)
        }

    })

更新 2

所以我最终将代码放入我的编辑器中,并注意到您没有在验证器中返回您的承诺,这可能是您遇到一切异步的原因。我添加了评论以向您展示我在哪里返回了承诺。希望这对你现在有用:)

router.route('/registration')
    .get(function(req, res) {
        UserModel.find({}, (err, users) => {
            if (err) res.status(500).send(err)
            res.json(users)
        })
    })
    .post(body('username').custom(value => {      
        return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
          if (user) {
              return Promise.reject('E-mail already in use');
          }
        });
    }), async(req, res, next) => {
        try {
            let newUser = new UserModel(req.body)
            let savedUser = await newUser.save(err => {
              if (err) 
                return res.json({ success: false, error: err })
              return res.json({ success: true })
            })

            if (savedUser)
              return res.redirect('/users/registration?success=true');

            return next(new Error('Failed to save user for unknown reasons'))
        } catch (err) {
            return next(err)
        }
    })

module.exports = router

【讨论】:

  • 谢谢尼克!我回家后试试看!
  • 嘿尼克!感谢您的帮助,但基于console.log's 其反向工作?!它试图保存request,然后检查电子邮件是否存在......?
  • 抱歉回复晚了。上周末我出城了。希望更新 2 可以解决您的问题 :)
  • { success: true } 即使出现错误,您是否会返回此内容。
  • 太有帮助了!我曾假设 API 中的 res.send() 会自动返回。我想它没有。在我希望 API 停止的地方添加一个 return 语句为我修复了 API 错误。
【解决方案2】:

明确返回您的 API 响应。所以不要简单的说res.json(users),而是改成return res.json(users)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 2020-11-14
    • 2020-11-02
    • 2021-03-05
    • 1970-01-01
    • 1970-01-01
    • 2019-12-11
    相关资源
    最近更新 更多