カスタム DataAnnotationsAttribute を使用して、モデルの DTO プロパティでさまざまな電話番号プロパティを検証したいと考えています。コードを DRY に保つために、DataAnnotations を ViewModel に複製したくありません。代わりに、DataAnnotationsModelValidatorProvider を使用してクライアント側の検証用のカスタム アダプターを登録しました。このアダプターは、通常 RemoteAttribute によって使用される ModelClientValidationRemoteRules を提供します。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);
}