Как применить проверку типа поля в Mongoose.js, не получая CastError?

Я пытаюсь использовать Express и Mongoose для создания простой формы, которая позволяет пользователю добавлять новые элементы в коллекцию.

Схема мангуста:

var Item = new Schema({
  name   : {type : String, required : true},
  price  : {type : Number, required : true},
  description : {type: String, required: true},
});

Экспресс маршрут:

var create = function(req, res) {
  Item.create(req.body, function(err) {
    if (err) return res.render('admin/items/new', {title: "New Item", errors: _.values(err.errors)});
    res.redirect('/admin/items/index');
  });
};

Пока достаточно просто. Что я хочу сделать, так это добавить соответствующую проверку, чтобы убедиться, что price, введенное пользователем, является числовым, а если не показать соответствующую ошибку.

С приведенным выше кодом, если пользователь вводит нечисловые символы в текстовое поле цены, я получаю CastError.

Итак, я попытался добавить следующую проверку в свою схему:

Item.path('price').validate(function(v, fn) {
  if (typeof v === 'number' || v === undefined) return fn(true);
  return fn(false);
}, "must-be-numeric");

Однако кажется, что Mongoose пытается привести значение перед применением этой проверки, поэтому для этого невозможно вернуть соответствующую ошибку проверки, вы все равно просто получите CastError. Кроме того, CastError предотвращает выполнение любых проверок других полей.

Я думаю, что то, что я пытаюсь сделать, довольно распространено (в Rails вы просто добавляете validate :price, :numericality => true к своей модели, и все).

Есть ли очевидный шаблон, который я упускаю из виду, который облегчает такую ​​проверку типа?

Как это делают все остальные?


Вещи, которые я пробовал/думал:

  • Я понимаю, что мог бы использовать дополнительную библиотеку проверки, такую ​​​​как node-validator, для «предварительной проверки» полей формы перед их передачей в Mongoose. Тем не менее, я действительно не хочу, чтобы дублирование кода или шаблонный беспорядок объединения объектов ошибок из двух валидаторов давали единый набор ошибок, который будет возвращен пользователю.
  • В идеале я бы использовал элемент «число» HTML5 input, чтобы предотвратить ошибочный ввод данных пользователем, но, учитывая, что Firefox и IE не поддерживают его, это еще не начало.

person tomtheguvnor    schedule 16.07.2012    source источник


Ответы (3)


Я знаю, что это очень старый пост, и мой ответ может быть вам не очень полезен, но все же я публикую его для пользователей, которые собираются его использовать.

Лично я бы рекомендовал использовать node-validator после того, как пользователь отправил свои данные (т.е. даже до того, как данные подчиняется мангусту). Таким образом, вы можете проверить сам пользовательский ввод, что является более безопасной идеей. Во-вторых, это создает четкий код, поскольку мангуст просто позволяет вам обрабатывать базу данных, а не всю пользовательскую проверку, которую вы искали.

Вы можете использовать express-validator, который является промежуточным программным обеспечением, использующим node-validator. Это очень хороший способ обработки проверки.

Хотя это задокументировано, я лично не думаю, что предварительное сохранение на самом деле предназначено для проверки. Его в основном используют для создания любых данных, на которые ссылаются, или для генерации событий для использования в других частях вашего кода.

Я надеюсь, что вы также найдете это полезным.

person Saransh Mohapatra    schedule 03.07.2013

Промежуточное ПО Mongoose — ваш друг.

Item.pre('save' function(next) {
  if (this.price) {
    if (!myValidationFunction(this.price)) {
      return next(new Error('Price must be yada yada yada'))    
    }
  }
  next()
})

События pre('save'... запускаются перед проверкой на уровне мангуста, поэтому у вас гораздо больше контроля над тем, как генерируются ошибки. Затем вы настраиваете универсальное промежуточное программное обеспечение для обработки ошибок в экспресс, которое перехватывает все неперехваченные ошибки и ошибки форматирования. сообщения, как вы хотите. В Express 2.x было событие .on('error'..., но оно было удалено в Express 3.

По сути промежуточное ПО для перехвата ошибок делает что-то вроде этого

app.use(function(err, req, res, next) {
  if (!(err instanceof Error)) next()
  // First param was some kind of error
  // format and send err.message however you like
  // in dev mode err.stack is usually interesting
  // this code is usually terminal, and does not call next()

})

Пара трюков, используемых при использовании промежуточного программного обеспечения мангуста, которые недостаточно документированы:

Во-первых, чтобы не удалось сохраниться, просто передайте объект Error в next. База данных не будет затронута. Во-вторых, вы можете различать вставки и обновления, проверяя свойство this.isNew.

Одним из преимуществ использования такого шаблона является то, что его используют большинство популярных библиотек узлов, и поэтому вы можете консолидировать обработку ошибок с некоторой уверенностью, что независимо от того, что пойдет не так, вы получите возможность отформатировать что-то разумное.

И последнее замечание: если вы примете этот подход в целом, вы можете захотеть создать свои собственные пользовательские объекты ошибок в node. Они отлично работают, но их немного сложно настроить. Вот хорошая статья: http://dustinsenos.com/articles/customErrorsInNode.

Надеюсь, это поможет!

person georgesn    schedule 17.07.2012
comment
Спасибо - я вижу, что использование промежуточного программного обеспечения Mongoose поможет решить проблему, но это все еще обходной путь. Я хочу иметь возможность подать заявку, а затем сразу сообщить о проверке моей модели. С подходом промежуточного программного обеспечения я просто получу одну ошибку от своего пользовательского промежуточного программного обеспечения. Я также заметил, что если вы попытаетесь присвоить неправильно типизированную переменную пути модели мангуста, модель просто проигнорирует это назначение. ИМХО пахнет такой тихой неудачей. - person tomtheguvnor; 17.07.2012
comment
Это действительно не игнорирует назначение. Он просто скрывает это, что должно быть еще хуже. - person numbers1311407; 30.01.2013

Есть еще один способ сделать это. Вы можете использовать настраиваемый сеттер и настраиваемый валидатор:

var setNumberOrUndefined = function (val) {

    // this prevents set undefined if the user did not
    // enter any value
    if (val == '')
        return null

    // Return undefined prevents CastError
    // now a validator must validate if it's a number or not
    var v = Number(val)
    return (isNaN(v))? undefined : v

}

var isNumberOrEmpty = function (val) {

    // This prevents return false if the user did not
    // enter any value
    if (val === null)
        return true
    else
        return 'number' == typeof val
}

var Item = new Schema({
    name   : {type : String, required : true},
    price  : {
        type : Number, 
        required : true,
        set: setNumberOrUndefined,
        validate: [
            {validator: isNumberOrEmpty, msg: 'Price must be numeric!'},
        ]
    },
    description : {type: String, required: true},
});
person Danilo Aburto Vivians    schedule 15.12.2013