今天介紹在ASP.NET MVC實現Validation驗證器擴展,通過使用Controller驗證并不是最好的方法:驗證過于分散,容易造成重復代碼,不利于維護與擴展,因此本節將使用MVC默認綁定器(DefaultModelBinder)中包含了驗證架構,并實現Validation驗證器擴展,請看下文:
MVC中可直接在控制器Action方法中進行驗證:檢查傳入參數,如果傳入參數不符合業務規則,則通過控制器的ModelState屬性的AddModelError方法向模型狀態添加錯誤消息,通過ModelState.IsValid判斷模型是否全部通過驗證,隨后,在視圖中通過Html.ValidationSummary、Html.ValidationMessage、Html.ValidationMessageFor輔助方法生成驗證消息。
驗證應該是和模型緊密相關的,如果我們將模型獨立為一個單獨的程序集,那么驗證也應該包含在模型的程序集中。從用戶錄入的數據到具體的模型,這個映射過程就是模型綁定,所以在綁定過程中實現驗證是一個不錯的選擇,這也是MVC中最重要的驗證方式。
MVC默認綁定器(DefaultModelBinder)中包含了驗證架構,具體來說,默認綁定器在將值提供器的數據填充到模型的時候,遵循以下流程:調用默認綁定器的OnModelUpdating方法—>從值提供器獲取值并填充到模型—>調用默認綁定器的OnModelUpdated方法。DefaultModelBinder類中OnModelUpdating方法始終返回true,即默認綁定器在填充模型之前沒有驗證,在填充數據后,OnModelUpdated中實現相應的驗證。默認綁定器中驗證體系類關系圖如下:
模型綁定中的驗證體系包含三個主要類:
驗證器提供者:ModelValidatorProvider,用于產生驗證器
驗證器:ModelValidator,用于實現具體的驗證邏輯,其中GetClientValidationRules用于返回客戶端驗證腳本使用的驗證規則(ModelClientValidationRule),Validate方法用于實現服務端的驗證邏輯。
特性:通常由提供者使用,根據特性指定的規則產生相應的驗證器
ModelValidatorProviders是一個靜態類,他包含一個用于保存MVC中默認驗證器提供者的集合(靜態屬性Proviers),MVC在填充完模型數據之后,依次對每一個模型屬性,從Providers中獲取所有的針對該屬性的驗證器,然后調用驗證器上的Validate方法產生驗證結果(ModelValidationResult),綁定器根據該結果數據向ModelState中添加驗證消息。
MVC中實現了三個默認的驗證器提供者(相應產生三個驗證器):
DataAnnotationsModelValidatorProvider: 用于實現.NET中的驗證特性,即System.ComponentModel.DataAnnotations命名空間下的多種驗證特性,包含用于限制屬性區間的RangeAttribute,用于驗證數據是否符合正則表達式的RegularExpressionAttribute,用于指定必需項的RequiredAttribute,用于限制字符串長度的StringLengthAttribute,DataAnnotationsModelValidatorProvider通過橋接模式,將.NET的驗證特性轉換為ModelValidator,所以我們可以直接在MVC中使用.NET驗證特性來實現驗證。
DataErrorInfoClassModelValidatorProvider: 此提供器主要是為了向后兼容,用于實現基于IDataErrorInfo接口的驗證方式(MVC 1.0),即你可以為模型實現IDataErrorInfo接口,這樣默認綁定器同樣可以通過該接口來調用你的驗證邏輯。
ClientDataTypeModelValidatorProvider: 此提供器只用于客戶端對數值型數據的驗證(產生相應的客戶端驗證腳本),他的Validate方法不會返回任何驗證結果。
MVC中另外實現了一個抽象類:AssociatedValidatorProvider,它從ModelValidatorProvider繼承,并重寫了GetValidator方法,增加傳入了附加在模型屬性上的特性集合。所以,如果我們需要實現基于特性的驗證方式,則應該從此類繼承實現自己的驗證器及相應的提供者類。當然我們也可以使用默認的DataAnnotationsModelValidatorProvider,這樣我們只需要從ValidationAttribute特性繼承,并實現自己的驗證邏輯。
客戶端驗證
ModelValidator中GetClientValidationRules方法可以返回用于客戶端的驗證規則,這些規則可以在客戶端腳本中訪問,客戶端腳本根據這些驗證規則檢查用戶錄入資料,并將驗證結果反饋給用戶。
下例將實現一個ConfirmValidator,用于驗證用戶注冊時兩次密碼必須輸入一致:
1、創建一個空MVC項目
2、添加用戶信息模型UserInfo.cs
顯示行號 復制代碼 ?UserInfo
public classUserInfo
{
public stringUserName { get; set; }
public stringPassword { get; set; }
public stringConfirmPassword { get; set; }
public stringEmail { get; set; }
}
3、創建一個特性,用于指定與屬性關聯的另一個屬性的名稱
顯示行號 復制代碼 ?ConfirmValidatorAttribute
public classConfirmValidatorAttribute: Attribute
{
publicStringConfirmPropertyName { get; set; }
publicConfirmValidatorAttribute(stringname)
{
ConfirmPropertyName = name;
}
}
4、創建用于實現驗證邏輯的ConfirmValidator類
顯示行號 復制代碼 ?ConfirmValidator
public classConfirmValidator: ModelValidator
{
private stringconfirmPropertyName;
publicConfirmValidator(ModelMetadatametaData, ControllerContextcontext, stringconfirmProperty)
: base(metaData, context)
{
confirmPropertyName = confirmProperty;
}
public overrideIEnumerable Validate(objectcontainer)
{
if(container == null)
yield break;
PropertyInfopi = container.GetType().GetProperty(confirmPropertyName);
if(pi != null)
{
stringconfirmValue = (string)pi.GetValue(container, null);
if( !(Metadata.Model??String.Empty).Equals(confirmValue??String.Empty))
{
yield return newModelValidationResult()
{
Message = "兩次輸入不一致!"
};
}
}
else
{
throw newInvalidOperationException("屬性"+ confirmPropertyName + "不存在");
}
}
}
5、創建用于產生ConfirmValidator的提供者類:ConfirmValidatorProvider
顯示行號 復制代碼 ?ConfirmValidatorProvider
public classConfirmValidatorProvider: AssociatedValidatorProvider
{
protected overrideIEnumerable GetValidators(ModelMetadatametadata, ControllerContextcontext, IEnumerable attributes)
{
foreach(ConfirmValidatorAttributeattr inattributes.OfType())
{
yield return newConfirmValidator(metadata, context, attr.ConfirmPropertyName);
}
}
}
6、創建用于測試的控制器及視圖
顯示行號 復制代碼 ?HomeController
public classHomeController: Controller
{
publicActionResultIndex()
{
returnView(newUserInfo());
}
[HttpPost]
publicActionResultIndex(UserInfoui)
{
returnView(ui);
}
}
x) %>
7、修改UserInfo.cs,在ConfirmPassword屬性上添加ConfirmValidator特性。
[ConfirmValidator("Password")]
public stringConfirmPassword { get; set; }
8、在Global Application_Start中添加ConfirmValidatorProvider
ModelValidatorProviders.Providers.Add(newConfirmValidatorProvider());
實現客戶端驗證:
1、修改ConfirmValidator類,添加GetClientValidationRules方法。
public overrideIEnumerable GetClientValidationRules()
{
ModelClientValidationRulerule = newModelClientValidationRule()
{
ErrorMessage = "兩次輸入不一致!",
ValidationType = "ConfirmValidator"
};
rule.ValidationParameters["ConfirmPropertyName"] = confirmPropertyName;
yield returnrule;
}
2、修改Index.aspx,添加對Ajax腳本的引用
3、添加自定義驗證腳本
Sys.Mvc.ValidatorRegistry.validators.ConfirmValidator = function (rule) {
var propertyName = rule.ValidationParameters.ConfirmPropertyName;
return function (value, context) {
var confirmValue =document.getElementsByName(propertyName)[0].value;
return (value == confirmValue);
}
};
4、開啟客戶端驗證功能
5、將ConfirmPassword屬性加入客戶端驗證
x.ConfirmPassword); %>
修改后完整視圖代碼:
x.ConfirmPassword); %>
x) %>
Sys.Mvc.ValidatorRegistry.validators.ConfirmValidator = function(rule) {
varpropertyName = rule.ValidationParameters.ConfirmPropertyName;
return function(value, context) {
varconfirmValue = document.getElementsByName(propertyName)[0].value;
return(value == confirmValue);
}
};
驗證用于保證用戶輸入的正確性,及時阻止用戶提交錯誤數據,確保數據符合業務規則。為此實現自定義擴展的驗證,其實也是一件相當必要的事情。