Node.js 在Joi中返回多个错误

Node.js 在Joi中返回多个错误,node.js,express,hapijs,joi,Node.js,Express,Hapijs,Joi,我试图从我的Joi验证模式返回多个自定义错误消息。这是模式 const Joi = require("@hapi/joi"); const string = Joi.string(); const emailSchema = string.email(); const usernameSchema = string .min(3) .max(30) .error(() => "Username must be between 3 and 30 characters"); con

我试图从我的Joi验证模式返回多个自定义错误消息。这是模式

const Joi = require("@hapi/joi");
const string = Joi.string();
const emailSchema = string.email();
const usernameSchema = string
  .min(3)
  .max(30)
  .error(() => "Username must be between 3 and 30 characters");
const passwordSchema = string
  .min(6)
  .error(() => "Password must be at least 6 characters");
const confirmPasswordSchema = Joi.valid(Joi.ref("passwordSchema")).error(
  () => "Passwords must match"
);

const localRegistrationSchema = Joi.object().keys({
  email: emailSchema.required().error(() => "Email is required"),
  username: usernameSchema.required().error(() => "Username is required"),
  password: passwordSchema.required().error(() => "Password is required"),
  confirmPassword: confirmPasswordSchema
});
const { error } = localRegistrationSchema.validate(req.body, {
        abortEarly: false
      });
console.log(error);
if (error) throw Boom.boomify(error);
这里是我使用模式的地方

const Joi = require("@hapi/joi");
const string = Joi.string();
const emailSchema = string.email();
const usernameSchema = string
  .min(3)
  .max(30)
  .error(() => "Username must be between 3 and 30 characters");
const passwordSchema = string
  .min(6)
  .error(() => "Password must be at least 6 characters");
const confirmPasswordSchema = Joi.valid(Joi.ref("passwordSchema")).error(
  () => "Passwords must match"
);

const localRegistrationSchema = Joi.object().keys({
  email: emailSchema.required().error(() => "Email is required"),
  username: usernameSchema.required().error(() => "Username is required"),
  password: passwordSchema.required().error(() => "Password is required"),
  confirmPassword: confirmPasswordSchema
});
const { error } = localRegistrationSchema.validate(req.body, {
        abortEarly: false
      });
console.log(error);
if (error) throw Boom.boomify(error);
但我一直遇到一个类型错误:无法读取未定义的属性“filter”,这似乎是由

details.push({
    message,
    path: item.path.filter((v) => typeof v !== 'object'),
    type: item.code,
    context: item.local
});
这是Joi错误处理代码的一部分

当我没有附加.error()部分时,我没有得到这个错误,但是如果我使用.error(新错误(“自定义错误消息”),我不能得到多个错误来显示


我不知道出了什么问题,也没有其他方法可以返回多条自定义错误消息来工作

您可以尝试类似的方法

email: emailSchema.required().error(() => {
    return {
      message: "Email is required."
    };
}),
错误 我调试了您的代码,仅返回
()=>“一些错误消息”
对您的解决方案不起作用。我们需要返回一个函数。您出现错误,因为自定义错误消息上的
路径
属性是
未定义的


错误链接不起作用 只有一条切换的错误消息有效

辅助函数 我的解决方案的核心是以下函数。它返回一个
函数
,该函数返回一个自定义错误
对象

function simpleErrorMsgFunc(message, path) {
  return () => {
    return {
      toString: () => message,
      message,
      path,
    }
  };
}

整体解决方案 另外,我注意到您的
confirmPassword
属性不是必需的


看到这里仍然有很多视图,我想补充一点,这就是我目前使用Joi处理多个错误的方式。我只是将所有的验证/清理函数串在一起,然后不是使用switch语句,而是将所有自定义消息放在.messages({})的末尾。如果您在前端使用它,同样适用于yup。这使它比switch语句和错误消息帮助函数更简洁明了

const string = Joi.string();
const passPattern =
  "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*\\W)[a-zA-Z0-9\\S]{8,}$";

export const signupLocalSchema = Joi.object({
  email: string.email().trim().lowercase().required().messages({
    "string.email": "Not a valid email address.",
    "string.empty": "Email is required.",
  }),
  username: string.min(3).max(30).trim().lowercase().required().messages({
    "string.min": "Username must be between 3 and 30 characters.",
    "string.max": "Username must be between 3 and 30 characters.",
    "string.empty": "Username is required.",
  }),
  password: string.pattern(new RegExp(passPattern)).messages({
    "string.pattern.base":
      "Password must be at least 8 characters and contain at least 1 lowercase, 1 uppercase, 1 number and 1 special character.",
  }),
  confirmPassword: Joi.valid(Joi.ref("password")).messages({
    "any.only": "Passwords must match.",
  }),
});

尝试使用自定义标签,如“email:emailSchema.required().label('用户电子邮件')已经尝试过了,但是它只是改变了标准错误消息中的路径名;它实际上并没有改变整个错误消息。谢谢!解决方案可行,但这不是一个罕见的用例。如果这是检查多个错误的最佳解决方案,我不明白为什么joi如此流行。我修改了你的解决方案ion试图通过删除switch语句并多次检查同一路径(即多次检查同一密钥),使其更干净、更模块化每一个都在不同的模式上。这会产生更干净、更模块化的代码,但最终只会检查每个键的最后一个实例。如果您知道任何其他解决方案或库可以更好地处理此用例,请告诉我!没问题Joel Jacobsen:)我还认为,对于多个自定义错误消息,
joi
中应该有一个更简单的解决方案。不幸的是,我没有找到什么。如果我找到另一个更适合您需要的验证库,我会告诉您。好的,我认为问题是我试图使用服务器端验证将用户友好的消息返回到客户端,当我最好使用客户端验证供用户查看,并且只对安全/边缘案例进行服务器端验证,因此不需要担心自定义消息时(尽管如果这更容易一点,那还是很好)。Joel Jacobson我找到了一个更优雅的解决方案。您可以使用
Joi.any.messages()
选项。有关完整示例,请参见:谢谢!!这真是太棒了。但是,我确实发现了一个警告,它也会影响您提供的原始代码以及您链接的示例中的代码。执行string().required()时,它会抛出string.empty错误而不是any.required错误,因此检查any.required不起作用。您必须检查string.empty。我不知道其他类型是否也一样。
const string = Joi.string();
const passPattern =
  "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*\\W)[a-zA-Z0-9\\S]{8,}$";

export const signupLocalSchema = Joi.object({
  email: string.email().trim().lowercase().required().messages({
    "string.email": "Not a valid email address.",
    "string.empty": "Email is required.",
  }),
  username: string.min(3).max(30).trim().lowercase().required().messages({
    "string.min": "Username must be between 3 and 30 characters.",
    "string.max": "Username must be between 3 and 30 characters.",
    "string.empty": "Username is required.",
  }),
  password: string.pattern(new RegExp(passPattern)).messages({
    "string.pattern.base":
      "Password must be at least 8 characters and contain at least 1 lowercase, 1 uppercase, 1 number and 1 special character.",
  }),
  confirmPassword: Joi.valid(Joi.ref("password")).messages({
    "any.only": "Passwords must match.",
  }),
});