Node.js + Joi如何显示自定义错误信息?

Raz*_*Raz 22 node.js joi

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

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

我搜索了这个,只发现了问题.

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

这是我用来验证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
      });
    }
Run Code Online (Sandbox Code Playgroud)

另外,有没有办法Joi在客户端使用?

谢谢!

Rvy*_*dey 63

原答案

目前的方法(我个人觉得它更好)是使用.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'
Run Code Online (Sandbox Code Playgroud)

的使用.errors()不建议只是使用定制信息更新默认消息。

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

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


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

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


更新 2: joi 错误类型列表及其描述(.messages()例如 string.base、array.unique、date.min 等)可在此处获得


更新 3:Joi已从 hapi 项目转移到独立项目https : //www.npmjs.com/package/joi。确保您使用的是最新版本(或至少高于 v17)。

  • `类型错误:Joi.string(...).messages 不是函数` (6认同)

Chr*_*alo 36

使用模板

\n

我必须深入研究源代码才能找到如何进行与上下文相关的消息模板/格式设置的示例,因为它似乎没有记录:

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

使用模板化消息的示例:

\n
const Joi = require("joi");\n\nconst schema = Joi.object({\n  nested: Joi.object({\n    name: Joi.string().required().messages({\n      "any.required": "{{#label}} is required!!",\n      "string.empty": "{{#label}} can\'t be empty!!",\n    }),\n  }),\n});\n\nconst result = schema.validate({\n  nested: {\n    // comment/uncomment to see the other message\n    // name: "",\n  },\n});\n\nconsole.log(result.error.details);\n
Run Code Online (Sandbox Code Playgroud)\n

使用模板语法时,似乎传递的上下文值如下所示,尽管特定规则/验证器可能会传递更多上下文:

\n
{\n \xe2\x80\x8bkey: "name", // this key, without ancestry\n \xe2\x80\x8blabel: `"nested.name"`, // full path with dots as separators, in quotes\n \xe2\x80\x8bvalue: "", // the value that was validated\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 他们的文档太糟糕了...... (2认同)

Ash*_*dam 25

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.',
      };
    }),
};
Run Code Online (Sandbox Code Playgroud)

错误对象中,您可以获取错误类型并根据错误信息进行更改。

  • 这是不对的,因为 error() 必须返回一个 JS Error 对象。 (2认同)

Gui*_*rez 24

扩展Ashish Kadam的答案,如果您有许多不同的错误类型,则可以检查存在哪种错误,并相应地设置其消息:

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;
  }),
  // ...
});
Run Code Online (Sandbox Code Playgroud)

您可以在此处检查错误列表:Joi 14.3.1 API参考>错误>错误列表

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

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

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

  • 我最喜欢这个答案。 (3认同)
  • 我已经创建了一篇关于基于此答案向 Hapi 响应添加错误代码的博客文章。感谢您的解决方案。任何有兴趣的人都可以在 https://midnightcodr.github.io/2019/06/08/Add-error-code-s-to-Hapijs-validation-output/ 查看 (2认同)

Raz*_*Raz 15

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

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()
  ..
});
Run Code Online (Sandbox Code Playgroud)

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

  • “标签”仅覆盖错误消息中的键名。而其他解决方案显示了如何自定义错误消息。 (11认同)

Nit*_*Joy 11

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

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"})
  });
Run Code Online (Sandbox Code Playgroud)

您可以参考此消息对象键。

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]
    }
Run Code Online (Sandbox Code Playgroud)


Sam*_* Ek 9

你可以使用.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); 
  }
});
Run Code Online (Sandbox Code Playgroud)

  • 但它会绕过 `abortEarly` :( (2认同)

小智 5

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

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

休息将保持不变。

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

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

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

干杯!