如何使用 Fluent Validator return RuleForEach 验证消息中的特定 属性?
How to return a specific property in the message of a RuleForEach validation, using Fluent Validator?
假设我有这样的测试class:
public class TestClass
{
public Properties[] TestProperties { get; set; }
public Guid Id { get; set; }
public TestClass(Properties[] testProperties)
{
Id = Guid.NewGuid();
TestProperties = testProperties;
}
}
还有一个属性class如下:
public class Properties
{
public Guid Id { get; set; }
public string Name { get; set; }
public Properties(string name)
{
Name = name;
Id = Guid.NewGuid();
}
}
我需要验证 TestProperties
数组中我的属性 Name
的 none 是否为空,如下所示:
public class TestValidator : AbstractValidator<TestClass>
{
public TestValidator()
{
RuleForEach(x => x.TestProperties)
.Must(y => y.Name != string.Empty && y.Name != null)
.WithMessage("TestPropertie at {CollectionIndex}, can't be null or empty");
}
}
但是我不想 return 在验证消息中 return 失败 属性 的位置,我想 return 它是 Id
,我该怎么做那么?
是的,使用默认验证器可以将对象中的其他 属性 值注入到消息中。
This can be done by using the overload of WithMessage that takes a
lambda expression, and then passing the values to string.Format or by
using string interpolation.
有几种方法可以做到这一点。首先,根据您当前使用 Must
:
的实现
public class TestClassValidator : AbstractValidator<TestClass>
{
public TestClassValidator()
{
RuleForEach(x => x.TestProperties)
.Must(y => !string.IsNullOrEmpty(y.Name))
.WithMessage((testClass, testProperty) => $"TestProperty {testProperty.Id} name can't be null or empty");
}
}
我尽可能避免使用 Must
,如果您坚持使用内置验证器,您更有可能开箱即用地进行客户端验证(如果您正在使用它在网络应用程序中)。使用 ChildRules
可以让您使用内置验证器并获得使用流畅界面的好处:
public class TestClassValidator : AbstractValidator<TestClass>
{
public TestClassValidator()
{
RuleForEach(x => x.TestProperties)
.ChildRules(testProperties =>
{
testProperties.RuleFor(testProperty => testProperty.Name)
.NotNull()
.NotEmpty()
.WithMessage(testProperty => $"TestProperty {testProperty.Id} name can't be null or empty");
});
}
}
我已经将 verbosity/alignment 的 NotNull() 验证器包含在自定义错误消息中,但是不需要它,因为 NotEmpty() 将涵盖 null 或空的情况。
最后,如果是我,我可能会为 Properties
类型创建一个单独的验证器(应该是 Property
吗?)并使用 SetValidator
来包含它。拆分验证问题,一次定义类型的验证并使规则可重用,并使验证器更易于测试。我不打算在这里讨论这个问题,因为这超出了这个问题的范围,但下面的链接给出了如何做的例子。
子验证器 doco(SetValidator
用法)here and here
可以找到上述工作示例,包括测试 here。
我的处理方式略有不同,因为我想要一个可重用性更高的解决方案。 (我正在以类似的方式验证许多不同的 类)。将消息标识放在带有 Must<> 的扩展名中会将您与类型联系起来,并可能导致复制和粘贴。相反,我将一个 Func 作为参数传递给验证,returns 一个标识字符串并让调用者决定如何标识被验证的对象。
public static IRuleBuilderOptions<T, string> IsValidStringEnumAllowNullable<T>(this IRuleBuilder<T, string> ruleBuilder, IList<string> validValues, Func<T,string> identifierLookup)
{
return ruleBuilder.Must((rootObject, testValue, context) =>
{
context.MessageFormatter.AppendArgument("AllowableValues", string.Join(", ", validValues));
context.MessageFormatter.AppendArgument("Identifier", identifierLookup(rootObject));
return string.IsNullOrEmpty(testValue) || validValues.Contains(testValue, StringComparer.Ordinal);
}).WithMessage("{Identifier}{PropertyName} with value {PropertyValue} must be one of the allowable values: {AllowableValues}, or null or empty string");
}
然后是调用代码,其中我告诉特定的验证消息 'how' 以识别消息传递的对象:
base.RuleForEach(rq => rq.Thingies).ChildRules(x =>
{
x.RuleFor(f => f.MyProperty).IsValidStringEnumAllowNullable(ValidationStrings.AnArrayOfAllowedValues, f => $"Thing[{f.Id}] ");
});
这段代码的结果是
Thing[1234] My Property with value asdf must be one of the allowable values: Value1, ValidValue2, Somethingelse, or null or empty string
假设我有这样的测试class:
public class TestClass
{
public Properties[] TestProperties { get; set; }
public Guid Id { get; set; }
public TestClass(Properties[] testProperties)
{
Id = Guid.NewGuid();
TestProperties = testProperties;
}
}
还有一个属性class如下:
public class Properties
{
public Guid Id { get; set; }
public string Name { get; set; }
public Properties(string name)
{
Name = name;
Id = Guid.NewGuid();
}
}
我需要验证 TestProperties
数组中我的属性 Name
的 none 是否为空,如下所示:
public class TestValidator : AbstractValidator<TestClass>
{
public TestValidator()
{
RuleForEach(x => x.TestProperties)
.Must(y => y.Name != string.Empty && y.Name != null)
.WithMessage("TestPropertie at {CollectionIndex}, can't be null or empty");
}
}
但是我不想 return 在验证消息中 return 失败 属性 的位置,我想 return 它是 Id
,我该怎么做那么?
是的,使用默认验证器可以将对象中的其他 属性 值注入到消息中。
This can be done by using the overload of WithMessage that takes a lambda expression, and then passing the values to string.Format or by using string interpolation.
有几种方法可以做到这一点。首先,根据您当前使用 Must
:
public class TestClassValidator : AbstractValidator<TestClass>
{
public TestClassValidator()
{
RuleForEach(x => x.TestProperties)
.Must(y => !string.IsNullOrEmpty(y.Name))
.WithMessage((testClass, testProperty) => $"TestProperty {testProperty.Id} name can't be null or empty");
}
}
我尽可能避免使用 Must
,如果您坚持使用内置验证器,您更有可能开箱即用地进行客户端验证(如果您正在使用它在网络应用程序中)。使用 ChildRules
可以让您使用内置验证器并获得使用流畅界面的好处:
public class TestClassValidator : AbstractValidator<TestClass>
{
public TestClassValidator()
{
RuleForEach(x => x.TestProperties)
.ChildRules(testProperties =>
{
testProperties.RuleFor(testProperty => testProperty.Name)
.NotNull()
.NotEmpty()
.WithMessage(testProperty => $"TestProperty {testProperty.Id} name can't be null or empty");
});
}
}
我已经将 verbosity/alignment 的 NotNull() 验证器包含在自定义错误消息中,但是不需要它,因为 NotEmpty() 将涵盖 null 或空的情况。
最后,如果是我,我可能会为 Properties
类型创建一个单独的验证器(应该是 Property
吗?)并使用 SetValidator
来包含它。拆分验证问题,一次定义类型的验证并使规则可重用,并使验证器更易于测试。我不打算在这里讨论这个问题,因为这超出了这个问题的范围,但下面的链接给出了如何做的例子。
子验证器 doco(SetValidator
用法)here and here
可以找到上述工作示例,包括测试 here。
我的处理方式略有不同,因为我想要一个可重用性更高的解决方案。 (我正在以类似的方式验证许多不同的 类)。将消息标识放在带有 Must<> 的扩展名中会将您与类型联系起来,并可能导致复制和粘贴。相反,我将一个 Func 作为参数传递给验证,returns 一个标识字符串并让调用者决定如何标识被验证的对象。
public static IRuleBuilderOptions<T, string> IsValidStringEnumAllowNullable<T>(this IRuleBuilder<T, string> ruleBuilder, IList<string> validValues, Func<T,string> identifierLookup)
{
return ruleBuilder.Must((rootObject, testValue, context) =>
{
context.MessageFormatter.AppendArgument("AllowableValues", string.Join(", ", validValues));
context.MessageFormatter.AppendArgument("Identifier", identifierLookup(rootObject));
return string.IsNullOrEmpty(testValue) || validValues.Contains(testValue, StringComparer.Ordinal);
}).WithMessage("{Identifier}{PropertyName} with value {PropertyValue} must be one of the allowable values: {AllowableValues}, or null or empty string");
}
然后是调用代码,其中我告诉特定的验证消息 'how' 以识别消息传递的对象:
base.RuleForEach(rq => rq.Thingies).ChildRules(x =>
{
x.RuleFor(f => f.MyProperty).IsValidStringEnumAllowNullable(ValidationStrings.AnArrayOfAllowedValues, f => $"Thing[{f.Id}] ");
});
这段代码的结果是
Thing[1234] My Property with value asdf must be one of the allowable values: Value1, ValidValue2, Somethingelse, or null or empty string