Node.js + Joi, как отображать пользовательские сообщения об ошибках?

Кажется довольно простым проверить ввод пользователя в Node.js RESTapi с помощью Joi.

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

Я искал это в Google и обнаружил только проблемы.

Может кто-нибудь может дать решение для этого?

Это код, который я использую для проверки в системе Joi:

    var schema = Joi.object().keys({
      firstName: Joi.string().min(5).max(10).required(),
      lastName: Joi.string().min(5).max(10).required()
      ..
    });

    Joi.validate(req.body, schema, function(err, value) {
      if (err) {
        return catched(err.details); 
      }
    });

    function catched(reject) {
      res.json({
        validData: false,
        errors: reject
      });
    }

Кроме того, есть ли способ использовать Joi на стороне клиента?

Спасибо!


person Raz    schedule 10.02.2018    source источник
comment
Интересная статья об использовании Joi для сообщений об ошибках: github.com/hapijs/joi/issues/ 546   -  person Veve    schedule 10.02.2018


Ответы (13)


Исходный ответ:

Текущий способ (лично я считаю его лучше) - использовать .messages() (или .prefs({messages})).

const Joi = require('@hapi/joi');

const joiSchema = Joi.object({
  a: Joi.string()
    .min(2)
    .max(10)
    .required()
    .messages({
      'string.base': `"a" should be a type of 'text'`,
      'string.empty': `"a" cannot be an empty field`,
      'string.min': `"a" should have a minimum length of {#limit}`,
      'any.required': `"a" is a required field`
    })
});

const validationResult = joiSchema.validate({ a: 2 }, { abortEarly: false });
console.log(validationResult.error); // expecting ValidationError: "a" should be a type of 'text'

Использование .errors() не рекомендуется просто для обновления сообщения по умолчанию с помощью настраиваемого сообщение.

.prefs({ messages }) - это продуманный способ предоставить больше параметров в качестве предпочтений. Другие параметры предпочтений берутся непосредственно из параметров .validate ()

Прочтите также: https://github.com/hapijs/joi/issues/2158


Обновление 1: я увидел, что приведенное выше объяснение не сработало для некоторых людей, поэтому я добавил код, чтобы проверить себя. Проверьте это здесь: https://runkit.com/embed/fnfaq3j0z9l2

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


Обновление 2: список типов ошибок joi и их описание (для .messages() - например, string.base, array.unique, date.min и т. д.) доступен здесь.


Обновление 3: Джой перешел из проекта hapi в автономный режим: https://www.npmjs.com/package/joi. Убедитесь, что вы используете последнюю версию (или, по крайней мере, выше v17).

person Rvy Pandey    schedule 04.10.2019
comment
Какую версию Joi вы используете? Проверьте это здесь. Кроме того, в документации указано использование any.messages здесь - person Rvy Pandey; 13.10.2019
comment
Преимущество этого ответа в том, что он также правильно работает со сторонними пакетами, такими как react-hook-form - person Ian; 03.05.2020
comment
Внимание! Пакет @ hapi / joi теперь перемещен обратно в joi. Согласно сообщению авторов здесь: npmjs.com/package/@hapi/joi, joi покидает организацию @hapi и возвращается к 'joi' (github.com/sideway/joi / issues / 2411) - person Mastrianni; 28.09.2020
comment
@RvyPandey, это похоже на ответ, который мне нужен, но у меня проблемы. Я разместил новый вопрос по адресу stackoverflow.com/questions/66070468/. Спасибо за любую помощь! - person Dave Stein; 05.02.2021

Расширяя ответ Ашиша Кадама, если у вас много разных типов ошибок, вы можете проверить, какой тип ошибки присутствует, и установить его сообщение соответственно:

Joi v17.4.0

v17.4.0 использует err.code

var schema = Joi.object().keys({
  firstName: Joi.string().min(5).max(10).required().error(errors => {
    errors.forEach(err => {
      switch (err.code) {
        case "any.empty":
          err.message = "Value should not be empty!";
          break;
        case "string.min":
          err.message = `Value should have at least ${err.local.limit} characters!`;
          break;
        case "string.max":
          err.message = `Value should have at most ${err.local.limit} characters!`;
          break;
        default:
          break;
      }
    });
    return errors;
  }),
  // ...
});

Вы можете проверить список ошибок здесь: Joi 17.4 .0 Справочник по API ›Ошибки› Список ошибок

Joi v14.3.1

v14.3.1 использует err.type

var schema = Joi.object().keys({
  firstName: Joi.string().min(5).max(10).required().error(errors => {
    errors.forEach(err => {
      switch (err.type) {
        case "any.empty":
          err.message = "Value should not be empty!";
          break;
        case "string.min":
          err.message = `Value should have at least ${err.context.limit} characters!`;
          break;
        case "string.max":
          err.message = `Value should have at most ${err.context.limit} characters!`;
          break;
        default:
          break;
      }
    });
    return errors;
  }),
  // ...
});

Вы можете проверить список ошибок здесь: Joi 14.3 .1 Справочник по API ›Ошибки› Список ошибок

Также вы можете проверить ссылку any.error для получения дополнительной информации. Информация. Цитата из документов:

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

  • err can be:
    • an instance of Error - the override error.
    • a function(errors), taking an array of errors as argument, where it must either:
      • return a string - substitutes the error message with this text
      • return a single object or an Array of it, where:
        • type - optional parameter providing the type of the error (eg. number.min).
        • message - необязательный параметр, если указан template, содержащий текст ошибки.
        • template - необязательный параметр, если указан message, содержащий строку шаблона, использующую тот же формат, что и обычные языковые ошибки joi.
        • context - необязательный параметр, чтобы указать контекст вашей ошибки, если вы используете template.
      • возвращает Error - то же самое, что и при непосредственном предоставлении Error, но вы можете настроить сообщение об ошибке на основе ошибок.
  • options:
    • self - Boolean value indicating whether the error handler should be used for all errors or only for errors occurring on this property (true value). This concept only makes sense for array or object schemas as other values don't have children. Defaults to false.
person Guillermo Gutiérrez    schedule 12.02.2019
comment
мне больше всего понравился этот ответ. - person LondonGuy; 18.02.2019
comment
Я создал сообщение в блоге о добавлении кодов ошибок в ответ Hapi на основе этого ответа. Спасибо за решение. Любой желающий может проверить это на midnightcodr.github.io/2019/06/08/ - person Rico Chen; 08.06.2019
comment
отличный ответ! - кстати, я использовал case string.empty вместо case any.empty, говоря о версии 17.4.0 - person moshiah_now; 11.07.2021

Joi версии 14.0.0

const SchemaValidation = {
  coins: Joi.number()
    .required()
    .error(() => {
      return {
        message: 'Coins is required.',
      };
    }),
  challenge_name: Joi.string()
    .required()
    .error(() => {
      return {
        message: 'Challenge name is required.',
      };
    }),
  challengeType: Joi.string()
    .required()
    .error(() => {
      return {
        message: 'Challenge type is required.',
      };
    }),
  challengeDescription: Joi.string()
    .required()
    .error(() => {
      return {
        message: 'Challenge description is required.',
      };
    }),
};

В объекте ошибки вы можете получить, тип ошибки и изменить сообщение соответственно.

person Ashish Kadam    schedule 17.10.2018
comment
Это неправильно, поскольку error () должен возвращать объект JS Error. - person MiKr13; 24.02.2021
comment
не уверен, что это правильный путь, но похоже работает .error( err => { err[0].message= "Custom Error Message"; return err; } ) - person Kaushik R Bangera; 10.04.2021

Решение, которое я нашел, - установить:

var schema = Joi.object().keys({
  firstName: Joi.string().min(5).max(10).required().label("Your error message in here"),
  lastName: Joi.string().min(5).max(10).required()
  ..
});

Затем распечатайте label из переменной обратного вызова error

person Raz    schedule 10.02.2018
comment
Метка заменяет только имя ключа в сообщениях об ошибках. В то время как другие решения отображали, как настроить сообщение об ошибке. - person Idan Dagan; 20.05.2019

Вы можете использовать .error (new Error ('message')), и это работает для меня

var schema = Joi.object().keys({
  firstName: Joi.string().min(5).max(10).required().error(new Error('Give your error message here for first name')),
  lastName: Joi.string().min(5).max(10).required().error(new Error('Give your error message here for last name'))
  ..
});

Joi.validate(req.body, schema, function(err, value) {
  if (err) {
    console.log(err.message)
    return catched(err.message); 
  }
});
person Sameer Ek    schedule 22.02.2018
comment
Но это обойдет abortEarly :( - person Pedram marandi; 13.10.2019

Решение для добавления пользовательских сообщений: просто добавьте еще одну связанную функцию, чтобы генерировать ошибку при определении схемы.
В вашем случае

 firstName: Joi.string().min(5).max(10).required().error(new Error('I am a custom error and I know it!')),

Остальные останутся такими же.

Решение для использования Joi на стороне клиента (ваш второй вопрос)

Joi-Browser - это пакет, который позволяет использовать ту же схему на стороне клиента.

Вот интересное обсуждение, на которое вы можете посмотреть.

Ваше здоровье!

person Syeda Aimen Batool    schedule 08.05.2018

let schema = Joi.object ({foo: Joi.number (). min (0) .error (() => '"foo" требует положительного числа')});

Документы ссылка

person Max Sherbakov    schedule 25.05.2018

Вы также можете отображать сообщения для определенного свойства

const Joi = require('Joi');

const schema = Joi.object({
    username: Joi.string()
      .min(2)
      .max(30)
      .required()
      .pattern(new RegExp(/^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{0,29}$/))
      .message({"string.pattern.base":"Invalid username",
                "string.min":"minimum 2 character required",
                "string.max":"maximum 30 characters allowed"})
  });

Вы можете сослаться на это для ключей объекта сообщения.

messages: {
      'any.custom': [Object],
      'any.default': [Object],
      'any.failover': [Object],
      'any.invalid': [Object],
      'any.only': [Object],
      'any.ref': [Object],
      'any.required': [Object],
      'any.unknown': [Object],
      'string.alphanum': [Object],
      'string.base': [Object],
      'string.base64': [Object],
      'string.creditCard': [Object],
      'string.dataUri': [Object],
      'string.domain': [Object],
      'string.email': [Object],
      'string.empty': [Object],
      'string.guid': [Object],
      'string.hex': [Object],
      'string.hexAlign': [Object],
      'string.hostname': [Object],
      'string.ip': [Object],
      'string.ipVersion': [Object],
      'string.isoDate': [Object],
      'string.isoDuration': [Object],
      'string.length': [Object],
      'string.lowercase': [Object],
      'string.max': [Object],
      'string.min': [Object],
      'string.normalize': [Object],
      'string.token': [Object],
      'string.pattern.base': [Object],
      'string.pattern.name': [Object],
      'string.pattern.invert.base': [Object],
      'string.pattern.invert.name': [Object],
      'string.trim': [Object],
      'string.uri': [Object],
      'string.uriCustomScheme': [Object],
      'string.uriRelativeOnly': [Object],
      'string.uppercase': [Object]
    }
person Nithin K Joy    schedule 06.05.2021

Просто вызовите функцию message ():

firstName: Joi.string().message("Your custom message")
person Muslim Omar    schedule 19.04.2020

В последней версии используйте сообщение как.

var schema = Joi.object().keys({
  firstName: Joi.string().min(5).max(10).required().messages({
    "string.base": `"username" should be a type of 'text'`,
    "string.empty": `"username" cannot be an empty field`,
    "any.required": `"username" is a required.`,
  }),
  lastName: Joi.string().min(5).max(10).required().messages({
    "string.base": `"lastName" should be a type of 'text'`,
    "string.empty": `"lastName" cannot be an empty field`,
    "any.required": `"lastName" is a required.`,
  }),
  [...]
});
person Bilal Maher    schedule 15.07.2020
comment
у меня вопрос ... каковы условия для допустимого формата электронной почты - person hamidreza ghanbari; 07.11.2020

Для тех, у кого проблемы с

... сообщения - это не функция

ошибок, необходимо установить joi с npm install @hapi/joi и импортировать с @hapi/joi. Я совершил ошибку, установив joi без префикса @hapi/, и мне потребовалось некоторое время, чтобы найти ошибку.

person Csaba Varga    schedule 19.10.2019
comment
Кроме того, при необходимости модуля необходимо указать его как const Joi = require('@hapi/joi'), если Joi установлен глобально. - person Saunved Mutalik; 22.01.2020

Лучшее решение, которое я нашел, было:

Создайте промежуточное ПО для проверки JOI

Validator.js - вы можете создать свой собственный объект ошибки

const Joi = require('Joi');

module.exports = schema => (req, res, next) => {
  const result = Joi.validate(req.body, schema);
  if (result.error) {
    return res.status(422).json({
      errorCause: result.error.name,
      missingParams: result.error.details[0].path,
      message: result.error.details[0].message
    });
  }
  next();
};

В маршрутах или контроллере передайте эту функцию промежуточного программного обеспечения


const joiValidator = require('../utils/validator'); // Wherever you have declare the validator or middlerware

   const  userSchema = joi.object().keys({
    email : joi.string().email().required(),
    password : joi.string().required()
  });

  routes.routes('/').get(joiValidator(userSchema), (req, res) => {
    res.status(200).send('Person Check');
  });

person Siddharth Sunchu    schedule 28.05.2020

Использование шаблонов

Мне пришлось копаться в источнике, чтобы найдите пример того, как делать контекстно-зависимые шаблоны / форматирование сообщений, поскольку это не задокументировано:

messages: {
  'string.alphanum': '{{#label}} must only contain alpha-numeric characters',
  'string.base': '{{#label}} must be a string',
  'string.base64': '{{#label}} must be a valid base64 string',
  'string.creditCard': '{{#label}} must be a credit card',
  'string.dataUri': '{{#label}} must be a valid dataUri string',
  'string.domain': '{{#label}} must contain a valid domain name',
  'string.email': '{{#label}} must be a valid email',
  'string.empty': '{{#label}} is not allowed to be empty',
  'string.guid': '{{#label}} must be a valid GUID',
  'string.hex': '{{#label}} must only contain hexadecimal characters',
  'string.hexAlign': '{{#label}} hex decoded representation must be byte aligned',
  'string.hostname': '{{#label}} must be a valid hostname',
  'string.ip': '{{#label}} must be a valid ip address with a {{#cidr}} CIDR',
  'string.ipVersion': '{{#label}} must be a valid ip address of one of the following versions {{#version}} with a {{#cidr}} CIDR',
  'string.isoDate': '{{#label}} must be in iso format',
  'string.isoDuration': '{{#label}} must be a valid ISO 8601 duration',
  'string.length': '{{#label}} length must be {{#limit}} characters long',
  'string.lowercase': '{{#label}} must only contain lowercase characters',
  'string.max': '{{#label}} length must be less than or equal to {{#limit}} characters long',
  'string.min': '{{#label}} length must be at least {{#limit}} characters long',
  'string.normalize': '{{#label}} must be unicode normalized in the {{#form}} form',
  'string.token': '{{#label}} must only contain alpha-numeric and underscore characters',
  'string.pattern.base': '{{#label}} with value {:[.]} fails to match the required pattern: {{#regex}}',
  'string.pattern.name': '{{#label}} with value {:[.]} fails to match the {{#name}} pattern',
  'string.pattern.invert.base': '{{#label}} with value {:[.]} matches the inverted pattern: {{#regex}}',
  'string.pattern.invert.name': '{{#label}} with value {:[.]} matches the inverted {{#name}} pattern',
  'string.trim': '{{#label}} must not have leading or trailing whitespace',
  'string.uri': '{{#label}} must be a valid uri',
  'string.uriCustomScheme': '{{#label}} must be a valid uri with a scheme matching the {{#scheme}} pattern',
  'string.uriRelativeOnly': '{{#label}} must be a valid relative uri',
  'string.uppercase': '{{#label}} must only contain uppercase characters'
}

Пример использования шаблонного сообщения:

const Joi = require("joi");

const schema = Joi.object({
  nested: Joi.object({
    name: Joi.string().required().messages({
      "any.required": "{{#label}} is required!!",
      "string.empty": "{{#label}} can't be empty!!",
    }),
  }),
});

const result = schema.validate({
  nested: {
    // comment/uncomment to see the other message
    // name: "",
  },
});

console.log(result.error.details);

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

{
 ​key: "name", // this key, without ancestry
 ​label: `"nested.name"`, // full path with dots as separators, in quotes
 ​value: "", // the value that was validated
}
person Chris Calo    schedule 23.06.2021