概述 表單驗證的最終效果大家都懂,這裡不闡述了,主要從巨集觀角度說說blazor中表單驗證框架涉及到的類,以及它們是如何協作的,看完這個,再看官方文檔也許能更輕鬆點。 blazor中的驗證框架分為兩部分:基礎驗證框架 和 基於數據註釋Atrrbute的驗證器,當然也提供了很多擴展點。註意我們通常使用數 ...
概述
表單驗證的最終效果大家都懂,這裡不闡述了,主要從巨集觀角度說說blazor中表單驗證框架涉及到的類,以及它們是如何協作的,看完這個,再看官方文檔也許能更輕鬆點。
blazor中的驗證框架分為兩部分:基礎驗證框架 和 基於數據註釋Atrrbute的驗證器,當然也提供了很多擴展點。註意我們通常使用數據註釋Atrrbute的驗證器,但它僅僅是在基礎驗證框架上擴展而來的,並不是核心,我們下麵先分析基礎驗證框架,後續再說基於數據註釋的驗證。
表單驗證是圍繞表單,往往一個表單綁定到一個對象,我這裡稱為編輯模型,表單中的輸入框與這個對象屬性綁定。
我們先把基礎驗證框架看作一個整體,從幾個角度分析:
- 產生驗證消息,這個不屬於驗證框架的核心部分,驗證框架只需要提供一個方法,當框架外部產生驗證時,調用此方法,讓驗證框架去記錄即可
- 存儲驗證消息是驗證框架的核心工作之一,我們可以想象使用一個Dictionary<欄位,List<驗證消息>>來存儲,key對應編輯模型的一個欄位,value是驗證消息集合,一個欄位可能有多個驗證消息。
- 我們可以定義兩個組件用於展示驗證消息,一個用於彙總展示,一個用戶展示指定指定的驗證消息,它們從驗證框架獲取驗證信息進行顯示。
- 清空驗證消息,驗證框架內部從存儲中清空即可,可以向外暴露個事件,還可以留個方法允許外部主動調用清空驗證消息,最好再允許外部清空指定欄位的驗證消息。
- 下麵說明下blazor中表單驗證的關鍵類,以及它們之間的關係。
基礎驗證框架先是定義幾個零散的類,各自負責處理自己的部分,然後通過引用、方法調用把幾個類串聯起來。
欄位標識FieldIdentifier
前面說存儲驗證消息時,類似這麼個類型:Dictionary<欄位,List<驗證消息>>,簡單的情況“欄位”可以用個string類型,但我們是在記憶體中,可以用更直接的方式。
FieldIdentifier = 編輯模型的引用 + 欄位名,它就代表編輯模型的某個欄位。後續我們要操作某個欄位,往往都是用這個參數。
驗證消息存儲器ValidationMessageStore
ValidationMessageStore是用這樣一個欄位Dictionary<FieldIdentifier, List<string>> _messages來存儲驗證消息的。key是欄位,value是此欄位的多個驗證消息。也提供了插入、刪除等驗證消息等方法。
它一般是有我們自己的代碼new出來的,或者有擴展的驗證器,如 基於數據註釋的驗證器new出來的。new它的時候會把editContext對象傳進來,後面會用。
當調用這個對象添加驗證消息時,它會存儲此欄位的驗證消息,並且調用editContext獲取欄位狀態FieldState,將自身的引用傳遞給它,以便FieldState將來反向通過存儲器來查詢驗證狀態。
欄位狀態FieldState
FieldState表示欄位的狀態,所謂的狀態就是 這個欄位是否修改過、以及獲取它關聯的驗證消息(本質上是從ValidationMessageStore獲取,後面會說)。
為啥不跟欄位標識FieldIdentifier合併為一個類呢?
欄位標識只是代表某個欄位,在很多方法參數時都會用到,它比較輕量;而欄位狀態是存儲了欄位是否修改和驗證消息列表的,相對來說比較重,那些方法可能僅僅是想通過欄位標識做查找,並不關心內部的狀態
它包含一個ValidationMessageStore的列表,獲取此欄位的驗證消息時,就是遍歷這個列表獲取的。但FieldState不負責向存儲器添加驗證消息。這個列表如何來的,驗證消息存儲器ValidationMessageStore已經說明瞭。
表單編輯上下文EditContext
可以把EditContext理解為 :欄位狀態FieldState列表 + 編輯模型實例,註意它不引用ValidationMessageStore,之所以這樣設計是想保持EditContext輕量。
那主要關心的就是FieldState列表的crud操作
創建FieldState,則是外部想通過EditContext獲取欄位狀態時,若有就返回,否則創建並記錄下來。
讀取就直接遍歷FieldState咯。
刪除:好像木有,也沒有必要
改:找到欄位狀態,直接改咯。
另外:一個EditForm與一個EditContext關聯,EditForm內部的組件會通過級聯方式獲得editContext的引用
額外的,它還提供欄位變化時、請求驗證時的相關事件。
小結
- 保持EditContext輕量,它不引用具體的驗證存儲器,而是只包含 欄位狀態 + 編輯模型實例,通過EditContext遍歷裡面的欄位狀態FieldState,獲取驗證消息,欄位狀態內部是遍歷存儲器列表獲取驗證消息。
- 將驗證狀態存儲器抽離出來,哪裡想向編輯上下文添加欄位驗證,就new ValidationMessageStore(editContext);然後Add驗證消息即可。
- 便於擴展,比如後面的基於數據註解的驗證器,它就是自己new ValidationMessageStore(editContext),然後使用註解驗證方式向其添加驗證。
驗證消息的展示
展示就比較簡單了,EditForm與EditContext關聯的,它會級聯傳遞到表單的子組件,子組件通過EditContext遍歷欄位就能拿到驗證消息。
ValidationSummary
它是彙總顯示所有驗證消息,它通過級聯參數獲取EditContext引用,遍歷然後顯示驗證消息。
ValidationMessage
原理類似,它顯示指定欄位的驗證消息。
基於數據註釋驗證器擴展
所謂的驗證器,無非是定義一個類,讓它持有EditContext的引用,然後它內部new ValidationMessageStore(editContext),然後通過自己的方式驗證後,Add驗證消息到ValidationMessageStore即可。
DataAnnotationsValidator
組件是個空殼,它通過級聯參數拿到EditContext對象,然後調用EditContextDataAnnotationsExtensions中定義的擴展方法,創建了一個實現IDispose的對象,組件釋放時會釋放這個對象
這個核心對象內部new ValidationMessageStore(editContext),用來存儲驗證消息。
它在初始化時會註冊:
_editContext.OnFieldChanged += OnFieldChanged;
_editContext.OnValidationRequested += OnValidationRequested;
欄位變化時,驗證,或者外部向editContext請求驗證時,觸發驗證。然後將驗證消息Add到存儲器中。