首页 > 解决方案 > ValidationError:用户验证失败:username_email:需要路径“username_email”。使用 express 验证器、next.js、express 和 mogoose

问题描述

我正在尝试在保存之前使用express-validator搜索 mongodb 以查看是否存在电子邮件。

这是我的路线的摘录:

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

但我收到了这个错误:

ValidationError: User validation failed: username_email: Path `username_email` is required.
    at new ValidationError (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongoose/lib/error/validation.js:30:11)
    at model.Document.invalidate (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongoose/lib/document.js:2292:32)
    at p.doValidate.skipSchemaValidators (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongoose/lib/document.js:2141:17)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongoose/lib/schematype.js:1037:9
    at process._tickCallback (internal/process/next_tick.js:61:11)
POST /users/registration 500 29.796 ms - 51

这是我的完整路线:

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

router
    .route('/registration')
    .get(function(req, res) {
        // req.session.visits = req.session.visits ? req.session.visits + 1 : 1
        UserModel.find({}, (err, users) => {
            if (err) res.status(500).send(err)
            res.json(users)
        })
    })
    .post(body('username').custom(value => {
        return UserModel.findOne({ 'username_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()

            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

这是我的架构/模型:

/* eslint-disable no-var */
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 },
        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)

为了更好地衡量,这是我的组件:

import React, { Component } from 'react'
import { Transition, Button, Form, Grid, Header, Message, Segment } from 'semantic-ui-react'

class Register extends Component {
  constructor(props) {
    super(props)

    this.state = {
      fadeUp: 'fade up',
      isLoggedIn: true,
      duration: 500,
      isVisible: false,
      username: '',
      password: '',
      usernameError: false,
      passwordError: false,
      formError: true,
      formSuccess: false
    }

    this.handleChange = this.handleChange.bind(this)
    this.handleBlur = this.handleBlur.bind(this)
    this.handleIsLoggedInClick = this.handleIsLoggedInClick.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  handleChange(event) {
    var { name, value } = event.target
    this.setState({
      [name]: value
    })
  }

  handleIsLoggedInClick() {
    this.state.isLoggedIn = this.state.isLoggedIn ? this.setState({ isLoggedIn: true }) : this.setState({ isLoggedIn: false })
  }

  handleBlur() {
    var { username, password, usernameError, passwordError } = this.state

    var mailFormat = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
    var error = false

    if ((!username.match(mailFormat)) && (!usernameError)) {
      this.setState({ usernameError: true })
      error = true
    } else {
      this.setState({ usernameError: false })
    }
  }

  handleSubmit(event) {
    event.preventDefault()

    var { username, password } = this.state

    var mailFormat = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
    var error = false

    if (!username.match(mailFormat)) {
      this.setState({ usernameError: true })
      error = true
    } else {
      this.setState({ usernameError: false })
    }

    if (password.length <= 8) {
      this.setState({ passwordError: true })
      error = true
    } else {
      this.setState({ passwordError: false })
    }

    console.log(`error ${error}`)
    if (error == false) {
      this.setState({ formError: false, formSuccess: true })
    }

    window.fetch('http://localhost:8016/users/registration', {
      method: 'POST',
      headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: username, password: password })
    }).then(function (response) {
      console.log(`response ${response}`)
      return response.json()
    }).then(function (data) {
      console.log('User created:', data)
    })
  }

  render() {
    var { username, password, usernameError, passwordError, formError, formSuccess, isLoggedIn } = this.state
    return (<div className='login-form' > {
      /*
                      Heads up! The styles below are necessary for the correct render of this example.
                      You can do same with CSS, the main idea is that all the elements up to the `Grid`
                      below must have a height of 100%.
                    */
    } <style > {`body > div, body > div > div, body > div > div > div.login-form { height: 100%;}`} </style>

      <Grid textAlign='center' style={{ height: '100%' }} verticalAlign='middle'>
        <Grid.Column style={{ maxWidth: 450 }}>
          <Header as='h2' color='teal' textAlign='center' > {isLoggedIn ? `Register for an account` : ` Log-in to your account`}</Header>

          <Form size='large' onSubmit={this.handleSubmit} error={(formError || formSuccess) || usernameError || passwordError} success={!formError && !formSuccess}>
            <Segment stacked >
              <Form.Input fluid icon='user'
                iconPosition='left'
                placeholder='E-mail address, e.g. joe@schmoe.com'
                name='username'
                value={username}
                onBlur={this.handleBlur}
                onChange={this.handleChange}
                error={usernameError}
              />

              <Transition visible={usernameError} animation='scale' duration={500}>
                <Message error content='Email is in incorrect format e.g. joe@schmoe.com' />
              </Transition>

              <Form.Input fluid icon='lock'
                iconPosition='left'
                placeholder='Password'
                name='password'
                value={password}

                onChange={this.handleChange}
                error={passwordError}
              />

              <Transition visible={passwordError} animation='scale' duration={500} >
                <Message error content='Paswword needs to be greater than eight characters.' />
              </Transition>

              <Button color='teal' fluid size='large' disabled={!this.state.username || !this.state.password} > {isLoggedIn ? `Register` : `Log-in`} </Button>

              <Transition visible={(!formError && formSuccess)} animation='scale' duration={500} >
                <Message success header='Your user registration was successful' content='You may now log-in with the username you have chosen' />
              </Transition>

            </Segment>
          </Form>

          {
            !isLoggedIn
              ? <Message >
                New to us ?
              <a onClick={this.handleIsLoggedInClick}
                  href='#' > Register! </a> </Message> : <Message >
                <a onClick={this.handleIsLoggedInClick}
                  href='#' > Back to Login </a> </Message>
          } </Grid.Column> </Grid> </div>
    )
  }
}
export default Register

标签: node.jsmongodbmongoosenext.jsexpress-validator

解决方案


看起来你的代码中有错字。当您向后端发出请求时,您将附加以下正文:

body: JSON.stringify({ email: username, password: password })

哪个有email参数。然后,在您的验证器中,您正在尝试验证usernameprop。

.post(body('username').custom(value => {

并且username在那里未定义。

所以,你必须遵循一个单一的命名约定,它应该可以工作。


推荐阅读