假設咱們書店須要錄入一本書,爲了簡單的體現咱們的自定義驗證,咱們的實體定義的很是簡單,就兩個屬性,一個名稱Name,一個出版社Publisher。ide
public class BookInfo { public string Name { get; set; } public string publisher { get; set; } }
Ok,需求有了,實體有了,那麼添加咱們的控制器和視圖。先把代碼貼出來。稍後咱們在作分析post
[HttpGet] public ActionResult Index() { return View(new BookInfo()); } //[HttpPost] public ActionResult Index(BookInfo book) { Validate(book); if (!ModelState.IsValid) { return View(book); } else { return Content("驗證未經過!"); } } private void Validate(BookInfo book) { if (string.IsNullOrEmpty(book.Name)) { ModelState.AddModelError("Name","Name必須"); } }
視圖就直接使用添加的強類型視圖便可。 this
其實最開始咱們可能想不到那麼多,多是這個樣子滴blog
public ActionResult Index(BookInfo book) { Validate(book); if (!ModelState.IsValid) { return View(book); } else { return Content("驗證未經過!"); } }
若是直接這麼寫,直接打開頁面,你會發現頁面顯示的時候就會出現咱們的校驗信息。顯然是不正確滴。
路由
分析一下,直接請求頁面的時候,顯然是經過Url輸入的Get請求訪問的咱們的Action,那麼這個時候是不須要校驗滴。
只有在咱們表單提交的時候才須要驗證,這個時候爲post的請求。也就是這麼一個action是搞不定的 ,咱們須要分開處理。get
剛纔咱們也分析了,處理時,實際上是根據請求動做來區分調用哪一個方法的,那麼咱們須要打上請求動做的標籤。string
若是沒有標籤咱們看看是什麼個狀況。報錯了。it
看看錯誤內容,方法調用不明確。這個實際上是路由解析的相關問題,關於路由解析,這裏就很少作解釋了,你們能夠簡單的理解爲,io
根據路由表,解析出咱們的Controller爲Home,Action爲Index,根據Action的名稱查找到兩個方法,此時運行時
只根據名稱沒法區這兩個方法,就會報錯了。
爲了讓運行時可以區分請求類型,咱們打上請求動做標籤。ok,如今能夠繼續了
這樣就完成了驗證。 class
在稍做深刻,ModelState是坨What,爲何直接用它的IsValid就能判斷校驗。它其實就是Controller的一個ModelStateDictionary類型的屬性。
如下是ModelStateDictionary定義
[Serializable] public class ModelStateDictionary : IDictionary<string, ModelState>, ICollection<KeyValuePair<string, ModelState>>, IEnumerable<KeyValuePair<string, ModelState>>, IEnumerable { public ModelStateDictionary(); public ModelStateDictionary(ModelStateDictionary dictionary); public void Add(KeyValuePair<string, ModelState> item); public void Add(string key, ModelState value); public void AddModelError(string key, Exception exception); public void AddModelError(string key, string errorMessage); public void Clear(); public bool Contains(KeyValuePair<string, ModelState> item); public bool ContainsKey(string key); public void CopyTo(KeyValuePair<string, ModelState>[] array, int arrayIndex); public IEnumerator<KeyValuePair<string, ModelState>> GetEnumerator(); public bool IsValidField(string key); public void Merge(ModelStateDictionary dictionary); public bool Remove(KeyValuePair<string, ModelState> item); public bool Remove(string key); public void SetModelValue(string key, ValueProviderResult value); public bool TryGetValue(string key, out ModelState value); IEnumerator IEnumerable.GetEnumerator(); public int Count { get; } public bool IsReadOnly { get; } public bool IsValid { get; } public ICollection<string> Keys { get; } public ICollection<ModelState> Values { get; } public ModelState this[string key] { get; set; } }
就看咱們用到的方法
public bool IsValid { get { return Enumerable.All<ModelState>((IEnumerable<ModelState>) this.Values, (Func<ModelState, bool>) (modelState => modelState.Errors.Count == 0)); } }
public void AddModelError(string key, string errorMessage) { this.GetModelStateForKey(key).Errors.Add(errorMessage); } private ModelState GetModelStateForKey(string key) { if (key == null) throw new ArgumentNullException("key"); ModelState modelState; if (!this.TryGetValue(key, out modelState)) { modelState = new ModelState(); this[key] = modelState; } return modelState; }
public class ModelState { private ModelErrorCollection _errors = new ModelErrorCollection(); public ValueProviderResult Value { get; set; } public ModelErrorCollection Errors { get { return this._errors; } } }
看看代碼,哦,基本上明白了,ModelState記錄了一個Errors集合,咱們校驗的時候會增長添加錯誤信息。
IsValid就判斷了字典中的全部ModelState的Error集是否都爲空。
再來猜一下Dictionary中的Key是什麼呢。猜想就應該是對應的校驗字段名。來作個試驗,把咱們添加錯誤信息的Name鍵,改成Publisher試試什麼效果。
嗯,跟預期的同樣。如今就明白了Mvc是如何爲咱們自定義校驗工做的了。