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

时间:2018-02-10 12:48:33

标签: node.js joi

使用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

谢谢!

14 个答案:

答案 0 :(得分:12)

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

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

答案 1 :(得分:11)

Joi版本14.0.0

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

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

答案 2 :(得分:8)

您可以使用 .error(新错误('消息')),并为我工作

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

答案 3 :(得分:7)

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

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

答案 4 :(得分:3)

  

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

文档link

答案 5 :(得分:3)

当前的方法(我个人认为更好)是使用.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 })是提供更多选项作为首选项的一种精心设计的方法。首选项的其他选项直接来自.validate()

的选项

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


更新:我看到上述解释对某些人没有用,所以我提供了一些代码来测试自己。在这里检查:https://runkit.com/embed/fnfaq3j0z9l2

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

答案 6 :(得分:3)

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

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

答案 7 :(得分:1)

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

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

休息将保持不变。

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

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

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

干杯!

答案 8 :(得分:1)

在最新版本中,将消息用作。

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

答案 9 :(得分:0)

let schema = Joi.object().keys({
   Joi.string().required().options({language: {any: {required: "First name is required"}}})
});

答案 10 :(得分:0)

对于任何人

有问题
  

...消息不是功能

错误,必须将joinpm install @hapi/joi一起安装,并使用@hapi/joi导入。我在安装joi时未加上前缀@hapi/时犯了一个错误,花了我一段时间才找到错误。

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

我发现的最佳解决方案是:

创建用于JOI验证的中间件

Validator.js-您可以创建自定义错误对象

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

'Update by Extendoffice: 2020/01/16
    Dim xCombox As OLEObject
    Dim xStr As String
    Dim xWs As Worksheet
    Dim xArr

'Combobox

    Set xWs = Application.ActiveSheet
    On Error Resume Next
    Set xCombox = xWs.OLEObjects("ComboBox1")
    With xCombox
        .ListFillRange = ""
        .LinkedCell = ""
        .Visible = False
    End With
    If Target.Validation.Type = 3 Then
        Target.Validation.InCellDropdown = False
        Cancel = True
        xStr = Target.Validation.Formula1
        xStr = Right(xStr, Len(xStr) - 1)
        If xStr = "" Then Exit Sub
        With xCombox
            .Visible = True
            .Left = Target.Left
            .Top = Target.Top
            .Width = Target.Width + 5
            .Height = Target.Height + 5
            .ListFillRange = xStr
            If .ListFillRange = "" Then
                xArr = Split(xStr, ",")
                Me.ComboBox1.List = xArr
            End If

            .LinkedCell = Target.Address
        End With
        xCombox.Activate
        Me.ComboBox1.DropDown

    End If


End Sub


Private Sub ComboBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    Select Case KeyCode
        Case 9 'Tab
            Application.ActiveCell.Offset(0, 1).Activate
        Case 13 'Enter
            Application.ActiveCell.Offset(1, 0).Select
            Exit Sub
    End Select
End Sub
Private Sub ComboBox2_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    Select Case KeyCode
        Case 9
            Application.ActiveCell.Offset(0, 1).Activate
        Case 13
            Application.ActiveCell.Offset(1, 0).Activate
    End Select
End Sub
Private Sub ComboBox2Description(Target As Range)

    Dim xCombox As OLEObject
    Dim xStr As String
    Dim xWs As Worksheet
    Dim xArr

    Set xWs = Application.ActiveSheet
    On Error Resume Next
    Set xCombox = xWs.OLEObjects("ComboBox2")
    With xCombox
        .ListFillRange = ""
        .LinkedCell = ""
        .Visible = False
    End With
    If Target.Validation.Type = 3 Then
        Target.Validation.InCellDropdown = False
        Cancel = True
        With xCombox
            .Visible = True
            .Left = Target.Left
            .Top = Target.Top
            .Width = Target.Width + 5
            .Height = Target.Height + 5
            .ListFillRange = "UniqueForDescriptionDropdown"
            .LinkedCell = Target.Address
        End With
        xCombox.Activate
        Me.ComboBox2.DropDown
    End If
End Sub

'Private Sub ComboBox1_LostFocus()

'Me.ComboBox1.Visible = False

'End Sub
Private Sub ComboBox2_LostFocus()

Me.ComboBox2.Visible = False

End Sub

在路由或控制器中传递此中间件功能

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

答案 13 :(得分:0)

使用模板

我不得不深入研究 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
}