【问题标题】:Node.js + Joi how to display a custom error messages?Node.js + Joi 如何显示自定义错误消息?
【发布时间】:2018-07-21 02:46:52
【问题描述】:

使用Joi 验证用户在 Node.js RESTapi 中的输入似乎非常简单。

但问题是我的应用程序不是用英文编写的。 这意味着我需要向前端用户发送自定义的书面消息。

我已经用谷歌搜索过,只是发现了问题。

也许有人可以为此提供解决方案?

这是我用来验证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

谢谢!

【问题讨论】:

标签: node.js joi


【解决方案1】:

我找到的解决方案是设置:

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()
  ..
});

然后从回调error 变量中打印label

【讨论】:

  • “标签”仅覆盖错误消息中的键名。而其他解决方案则展示了如何自定义错误消息。
【解决方案2】:

你可以使用 .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); 
  }
});

【讨论】:

  • 但它会绕过abortEarly :(
【解决方案3】:

添加自定义消息的解决方案: 只需添加另一个链式函数即可在定义架构时引发错误。
你的情况

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

其余部分保持不变。

在客户端使用 Joi 的解决方案(您的第二个问题)

Joi-Browser 是允许在客户端使用相同模式的包。

Here 是一个有趣的讨论,你可以看看。

干杯!

【讨论】:

    【解决方案4】:

    让 schema = Joi.object({ foo: Joi.number().min(0).error(() => '"foo" 需要一个正数') });

    文档link

    【讨论】:

      【解决方案5】:

      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.',
            };
          }),
      };
      

      errors 对象中,您可以获取错误类型和更改消息。

      【讨论】:

      • 这是不对的,因为 error() 必须返回一个 JS 错误对象。
      • 不确定这是否正确,但似乎可以工作.error( err => { err[0].message= "Custom Error Message"; return err; } )
      【解决方案6】:

      Ashish Kadam's answer上扩展,如果你有许多不同的错误类型,你可以检查存在哪种类型的错误,并相应地设置它的消息:

      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 Reference > Errors > List of errors

      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 Reference > Errors > List of errors

      您还可以查看any.error 参考以获取更多信息。引用文档:

      如果规则失败,则使用自定义错误覆盖默认的 joi 错误,其中:

      • err 可以是:
        • Error 的一个实例 - 覆盖错误。
        • function(errors),将错误数组作为参数,其中它必须:
          • 返回 string - 用此文本替换错误消息
          • 返回单个objectArray,其中:
            • type - 提供错误类型的可选参数(例如number.min)。
            • message - 如果提供了 template,则为可选参数,包含错误文本。
            • template - 如果提供了 message,则为可选参数,包含模板字符串,使用与通常的 joi 语言错误相同的格式。
            • context - 可选参数,如果您使用的是template,则为您的错误提供上下文。
          • 返回Error - 与直接提供Error 时相同,但您可以根据错误自定义错误消息。
      • options:
        • self - 布尔值,指示错误处理程序应用于所有错误还是仅用于此属性上发生的错误(true 值)。这个概念只对arrayobject 模式有意义,因为其他值没有子值。默认为false

      【讨论】:

      • 我最喜欢这个答案。
      • 我已经创建了一篇关于根据这个答案将错误代码添加到 Hapi 响应的博客文章。感谢您的解决方案。有兴趣的可以去midnightcodr.github.io/2019/06/08/…查看
      • 优秀的答案! - 顺便说一句,我使用案例“string.empty”而不是案例“any.empty”,说的是v17.4.0
      【解决方案7】:
      let schema = Joi.object().keys({
         Joi.string().required().options({language: {any: {required: "First name is required"}}})
      });
      

      【讨论】:

      • TypeError: Joi.string(...).messages is not a function
      【解决方案8】:

      原答案

      目前的方式(我个人觉得更好)是使用.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() 的用法是 not recommended,只是用自定义消息更新默认消息。

      .prefs({ messages }) 是一种精心设计的方式,可以提供更多选项作为首选项。 prefs 的其他选项直接取自.validate()的选项

      进一步阅读:https://github.com/hapijs/joi/issues/2158


      更新 1:我看到上面的解释对某些人来说并不奏效,所以我放了一些代码来测试自己。在这里查看:https://runkit.com/embed/fnfaq3j0z9l2

      还更新了之前共享的代码 sn-p 以包含从包包含、使用到调用实际验证方法的详细信息。


      更新 2:joi 错误类型列表及其描述(对于 .messages() - 如 string.base、array.unique、date.min 等)在 here 可用。


      更新 3:Joi 有 moved from hapi project 到独立:https://www.npmjs.com/package/joi。 确保您使用的是最新版本(或至少高于 v17)。

      【讨论】:

      • TypeError: Joi.string(...).messages is not a function
      • 您使用的是什么版本的 Joi?请查看here。此外,文档指定了any.messageshere的用法
      • 这个答案的好处是,它也可以与react-hook-form等第3方包一起正常工作
      • 请注意,@hapi/joi 包现在已移回 joi。根据作者在此处的消息:npmjs.com/package/@hapi/joi,“joi 将离开 @hapi 组织并回到 'joi' (github.com/sideway/joi/issues/2411)”
      • @RvyPandey 这看起来像是我需要的答案,但遇到了麻烦。我在stackoverflow.com/questions/66070468/… 发布了新问题。感谢您的帮助!
      【解决方案9】:

      对于任何有问题的人

      ...消息不是函数

      错误,您必须使用npm install @hapi/joi 安装joi,并使用@hapi/joi 导入它。我错误地安装了没有@hapi/ 前缀的joi,我花了一段时间才找到错误。

      【讨论】:

      • 另外,在需要模块时,如果您全局安装了 Joi,则必须将其指定为 const Joi = require('@hapi/joi')
      【解决方案10】:

      只需调用“message()”函数即可:

      firstName: Joi.string().message("Your custom message")
      

      【讨论】:

        【解决方案11】:

        我找到的最佳解决方案是:

        为 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');
          });
        
        

        【讨论】:

          【解决方案12】:

          在最新版本中使用 message as。

          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.`,
            }),
            [...]
          });
          

          【讨论】:

          • 我有疑问......电子邮件有效格式的条件是什么
          【解决方案13】:

          您还可以显示特定属性的消息

          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]
              }
          

          【讨论】:

            【解决方案14】:

            使用模板

            我不得不深入研究source 以找到一个如何对消息进行上下文相关模板/格式化的示例,因为它似乎没有记录在案:

            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
            }
            

            【讨论】:

            • 他们的文档太糟糕了...
            【解决方案15】:

            如果您需要一种简单的方式来传递自定义消息,请使用js-flex-validator

            import Flex, { validateObject } from "js-flex-validator";
            
            const constraints = [
              Flex("username")
                .string()
                .allowEmpty()
                .min(3, "Username should be at least 3 characters")
                .max(50, "Username should not exceeds 50 characters"),
              Flex("email")
                .email("This email is not valid.")
                .match(/\w.@edu.com$/, "Should be a edu.com domain")
                .required()
                .min(5, "Email should be at least 3 characters")
                .max(255, "Username should not exceeds 255 characters"),
              Flex("password")
                .string()
                .required()
                .min(5, "Password should be at least 5 characters")
                .max(20, "Password should not exceeds 50 characters"),
            ];
            
            const data = {username: "geek4", email: "example@email.com", password: "123456" };
            const { hasError, errorDetails } = validateObject(data, constraints)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-08-12
              • 2021-03-16
              • 1970-01-01
              • 1970-01-01
              • 2020-02-12
              • 2022-01-20
              • 1970-01-01
              • 2020-04-09
              相关资源
              最近更新 更多