Asp.net MVC源碼分析–Model Validation(Server端)實現(1)

來源:互聯網
上載者:User
一.MVC Validation 用法:

在Asp.net MVC 架構中如果需要對Model 對象加入驗證,我們可以在Model的屬性上標記所有繼承於ValidationAttribute的Attribute特性. 

例如下面的代碼中,StringLength/Range/Compare 都是繼承於ValidationAttribute類.

 public class LogOnModel
{
[Required]
[StringLength(10)]
public string UserName { get; set; }

[Required]
[Range(5,10)]
public string Password { get; set; }

[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }

[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}

在Action 中我們可以調用 ValidateModel 方法對Model對象進行驗證,如果驗證沒有通過則會拋出InvalidOperationException異常,同時ModelState.IsValid狀態為false

   [HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
this.ValidateModel(model);
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}

// If we got this far, something failed, redisplay form
return View(model);
}

這裡需要注意的是如果我們不調用ValidateModel 方法Action上的Model對象預設也是能夠驗證的,這是因為在綁定對象階段DefaultModelBinder.BindComplexElementalModel方法中MVC觸發了驗證的方法。

DefaultModelBinder.cs

 internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {
// need to replace the property filter + model object and create an inner binding context
ModelBindingContext newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model);

// validation
if (OnModelUpdating(controllerContext, newBindingContext)) {
BindProperties(controllerContext, newBindingContext);
OnModelUpdated(controllerContext, newBindingContext);
}
}
protected virtual void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) {
Dictionary<string, bool> startedValid = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(bindingContext.ModelMetadata, controllerContext).Validate(null)) {
string subPropertyName = CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName);

if (!startedValid.ContainsKey(subPropertyName)) {
startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);
}

if (startedValid[subPropertyName]) {
bindingContext.ModelState.AddModelError(subPropertyName, validationResult.Message);
}
}
}
二.ValidateModel方法源碼分析

下面我們看一下MVC 是如何?ValidateModel方法驗證的.

 1  protected internal void ValidateModel(object model) {
2 ValidateModel(model, null /* prefix */);
3 }
4
5 protected internal void ValidateModel(object model, string prefix) {
6 if (!TryValidateModel(model, prefix)) {
7 throw new InvalidOperationException(
8 String.Format(
9 CultureInfo.CurrentCulture,
10 MvcResources.Controller_Validate_ValidationFailed,
11 model.GetType().FullName
12 )
13 );
14 }
15 }
16
17 protected internal bool TryValidateModel(object model) {
18 return TryValidateModel(model, null /* prefix */);
19 }
20
21 protected internal bool TryValidateModel(object model, string prefix) {
22 if (model == null) {
23 throw new ArgumentNullException("model");
24 }
25
26 ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
27
28 foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(metadata, ControllerContext).Validate(null)) {
29 ModelState.AddModelError(DefaultModelBinder.CreateSubPropertyName(prefix, validationResult.MemberName), validationResult.Message);
30 }
31
32 return ModelState.IsValid;
33 }

我們可以看到第28行MVC是調用了ModelValidator.GetModelValidator 來擷取Model 的Validator 對象的.我們看一下這個方法的實現,它返回的是CompositeModelValidator對象.

   public static ModelValidator GetModelValidator(ModelMetadata metadata, ControllerContext context) {
return new CompositeModelValidator(metadata, context);
}
CompositeModelValidator.cs 
 1  private class CompositeModelValidator : ModelValidator {
2 public CompositeModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
3 : base(metadata, controllerContext) {
4 }
5
6 public override IEnumerable<ModelValidationResult> Validate(object container) {
7 bool propertiesValid = true;
8
9 foreach (ModelMetadata propertyMetadata in Metadata.Properties) {
10 foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(ControllerContext)) {
11 //Robbin:一但Validate返回了ModelValidationResult就證明驗證沒有通過.
12 foreach (ModelValidationResult propertyResult in propertyValidator.Validate(Metadata.Model)) {
13 propertiesValid = false;
14 yield return new ModelValidationResult {
15 MemberName = DefaultModelBinder.CreateSubPropertyName(propertyMetadata.PropertyName, propertyResult.MemberName),
16 Message = propertyResult.Message
17 };
18 }
19 }
20 }
21
22 if (propertiesValid) {
23 foreach (ModelValidator typeValidator in Metadata.GetValidators(ControllerContext)) {
24 foreach (ModelValidationResult typeResult in typeValidator.Validate(container)) {
25 yield return typeResult;
26 }
27 }
28 }
29 }
30 }
我們再看一下上面第10行,propertyMetadata.GetValidators這個方法的實現,他返回了所有的Validator的實現。並且在12行,調用這些實現的Validate方法進行驗證操作.
public virtual IEnumerable<ModelValidator> GetValidators(ControllerContext context) {
return ModelValidatorProviders.Providers.GetValidators(this, context);
}

它調用了ModelValidatorProviders.Providers對象的GetValidators 方法. 這個對象的類型是ModelValidatorProviderCollection接下來我們看一下ModelValidatorProviders.Providers的實現

  public static class ModelValidatorProviders {

private static readonly ModelValidatorProviderCollection _providers = new ModelValidatorProviderCollection() {
new DataAnnotationsModelValidatorProvider(),
new DataErrorInfoModelValidatorProvider(),
new ClientDataTypeModelValidatorProvider()
};

public static ModelValidatorProviderCollection Providers {
get {
return _providers;
}
}

}

這個對象初始化時預設包括了三個ValidatorProvider,在這裡我們需要著重注意的是DataAnnotationsModelValidatorProvider,它提供了所有系統預設的Validation規則(除了RemoteAttribute).

下一篇我們來詳細的分析一下DataAnnotationsModelValidatorProvider的實現以及使用的設計模式.

 

轉載請註明出處:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289228.html 

本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.