Controller.ViewData.ModelState 為 ModelStateDictionary,鍵為欄位名稱(含前綴),值為 ModelState:
[Serializable]
public class ModelState
{
public ModelErrorCollection Errors { get; } // 驗證錯誤集合
public ValueProviderResult Value { get; set; } // 繫結原始值
}
[Serializable]
public class ModelErrorCollection : Collection<ModelError>
{
public void Add(Exception exception);
public void Add(string errorMessage);
}
[Serializable]
public class ModelError
{
public ModelError(Exception exception);
public ModelError(string errorMessage);
public ModelError(Exception exception, string errorMessage);
public string ErrorMessage { get; }
public Exception Exception { get; }
}
Model 驗證發生於 Model 繫結之後、Action 執行之前。DefaultModelBinder 為每個屬性繫結值後,立即呼叫對應 ModelValidator 執行驗證,失敗則將 ModelError 加入 ModelState。
定義永遠失效的驗證屬性:
public class AlwaysFailsAttribute : ValidationAttribute
{
public override bool IsValid(object value) => false;
}
套用至巢狀 Model:
[AlwaysFails(ErrorMessage = "Contact")]
public class Contact
{
[AlwaysFails(ErrorMessage = "Contact.Name")] public string Name { get; set; }
[AlwaysFails(ErrorMessage = "Contact.PhoneNo")] public string PhoneNo { get; set; }
[AlwaysFails(ErrorMessage = "Contact.EmailAddress")] public string EmailAddress { get; set; }
[AlwaysFails(ErrorMessage = "Contact.Address")] public Address Address { get; set; }
}
[AlwaysFails(ErrorMessage = "Address")]
public class Address
{
[AlwaysFails(ErrorMessage = "Address.Province")] public string Province { get; set; }
[AlwaysFails(ErrorMessage = "Address.City")] public string City { get; set; }
[AlwaysFails(ErrorMessage = "Address.District")] public string District { get; set; }
[AlwaysFails(ErrorMessage = "Address.Street")] public string Street { get; set; }
}
提交表單後,ModelState 將包含所有層級的錯誤:
Contact 錯誤Contact.Name、Contact.PhoneNo、Contact.EmailAddressContact.Address 錯誤Contact.Address.Province、City、District、StreetControllerActionInvoker 呼叫 GetParameterValue 繫結參數。DefaultModelBinder.BindModel 遞迴繫結屬性。OnModelUpdated → Validate。Validate 透過 ModelValidatorProviders.Providers.GetValidators(metadata, context) 取得所有 ModelValidator。validator.Validate(container)。ModelValidationResult,轉為 ModelError 加入 bindingContext.ModelState。ModelValidationResult.MemberName = String.Empty。MemberName = 屬性名稱(含前綴,如 Address.Province)。ModelStateDictionary。Action 內可透過 ModelState.IsValid 判斷整體驗證結果,ModelState["Key"].Errors 取得特定欄位錯誤訊息。View 中 Html.ValidationMessageFor、Html.ValidationSummary 自動讀取顯示。
MVC 驗證系統以 ModelValidator 為核心,整合於 Model 繫結遞迴流程中,結果統一存入 ModelStateDictionary。理解此流程有助於自訂繫結器、驗證屬性或錯誤處理邏輯。