我想使用自定义 DataAnnotationsAttribute 验证模型的 DTO 属性上的各种电话号码属性。我不想将 DataAnnotations 复制到 ViewModel 上,以保持代码 DRY,而是使用 DataAnnotationsModelValidatorProvider 注册了一个用于客户端验证的自定义适配器。此适配器提供 ModelClientValidationRemoteRules,通常由 RemoteAttribute 使用。jQuery 不显眼的验证然后调用我的验证操作,它验证各个字段。
但是,这种设置还不够。
该属性当前使用其 ContainerType 来确定要调用哪个验证操作。然而,DTO 在不同嵌套级别的不同视图模型上使用,因此我们不确切知道在操作上使用什么前缀。根据模型层次结构中 ProfileDto 的位置,操作前缀需要更改
验证操作使用 Request.Form.Keys 来确定应该验证的属性。我知道为了单元测试等原因,最好远离 Action 中的 Request 对象。
有没有一种好方法可以在回发中包含要验证的字段名称,所以我可以在我的操作中将它作为附加参数而不是使用 Request.Form?
有没有办法让模型绑定器绑定我的属性,因为它们将使用取决于子模型名称的前缀回发?
提前致谢!
属性如下:
public class PhoneNumberAttribute : ValidationAttribute
{
public PhoneNumberType RequiredType { get; set; }
public PhoneNumberAttribute()
: base("{0} is not a valid phone number.")
{
}
public override bool IsValid(object value)
{
string s = value as string;
if (s == null)
{
return false;
}
if (!PhoneNumberUtils.IsValidNumber(s, RequiredType))
{
return false;
}
return true
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name);
}
}
和适配器:
public class PhoneNumberAttributeAdapter : DataAnnotationsModelValidator<PhoneNumberAttribute>
{
public PhoneNumberAttributeAdapter(ModelMetadata metadata, ControllerContext context, PhoneNumberAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var errorMessage = Attribute.FormatErrorMessage(Metadata.GetDisplayName());
var routeData = new RouteValueDictionary {
{ "controller", "Validate" },
{ "action", Metadata.ContainerType.Name },
};
var path = RouteTable.Routes.GetVirtualPathForArea(ControllerContext.RequestContext, routeData);
var rule = new ModelClientValidationRemoteRule(
errorMessage,
path.VirtualPath,
"POST",
"*." + Metadata.PropertyName);
return new[] { rule };
}
}
这是行动:
public ActionResult ProfileDto([Bind(Prefix = "Dto")]ProfileDto model)
{
string fieldToValidate = Request.Form.Keys[0];
if (ModelState.IsValidField(fieldToValidate))
{
return Json(true);
}
var fieldErrors = ModelState[fieldToValidate].Errors;
return Json(fieldErrors.First().ErrorMessage);
}