這是一篇4年前的文章:【經驗分享】在ASP.NET Core中,如果禁用某個請求的模型驗證?
事隔多年,又有網友問到這個問題。我就來重新整理一下,順便擴展一下之前的解決辦法。
=====
這是一個來自網友【David】的提問。在 AppBoxCore 項目的新增用戶頁面,新增一個上傳按鈕:
<f:FileUpload ID="filePhoto" ShowRedStar="false" ShowEmptyLabel="true" ButtonText="上傳個人頭像" ButtonOnly="true" Required="false" ButtonIcon="ImageAdd" OnFileSelected="@Url.Handler("filePhoto_FileSelected")" OnFileSelectedFields="filePhoto"> </f:FileUpload>
後臺代碼:
public IActionResult OnPostFilePhoto_FileSelected(IFormFile filePhoto, IFormCollection values) { if (filePhoto != null) { string fileName = filePhoto.FileName; // 。。。 } return UIHelper.Result(); }
此時上傳照片時,會彈出錯誤提示框(截圖所示)。
The 用戶名 field is required.
The 郵箱 field is required.
The 性別 field is required.
The 密碼 field is required.
這是因爲頁面模型中定義了一個綁定屬性:
[CheckPower(Name = "CoreUserNew")] public class UserNewModel : BaseAdminModel { [BindProperty] public User CurrentUser { get; set; } // ... }
而在 POST 請求中會觸發模型綁定,如果發現模型不完整就會報錯。這個錯誤提示框是由 FineUICore 框架處理的,無需自己寫代碼。
現在問題就來了!
====================
對於上述場景,僅僅是上傳圖片而無需驗證 CurrentUser 模型屬性,該如何處理呢?
其實也很簡單,只需要在處理器中清空模型狀態就可:
public IActionResult OnPostFilePhoto_FileSelected(IFormFile filePhoto, IFormCollection values) { ModelState.Clear(); if (filePhoto != null) { string fileName = filePhoto.FileName; // 。。。 } return UIHelper.Result(); }
Done!
====================
這個問題也確實讓我走了不少彎路,剛開始總想着如何禁用某些POST請求的模型驗證,想着微軟總能爲處理器(Handler)提供一些註解(Annotation)來吧,結果查了一圈沒有發現!
後來,想着換個思路:既然找不到禁用的方法,就清空模型狀態,結果也是一樣的。
延伸思考
============================
上面既然可以使用ModelState.Clear();清空所有的模型狀態,那是否也可以移除模型狀態中的某些屬性呢?
以用戶登錄表單爲例(用戶名+密碼),先看下模型定義:
namespace FineUICore.Examples.WebForms.Pages.DataModel.Models { public class User { [Required] [Display(Name = "用戶名")] [StringLength(20)] public string UserName { get; set; } [Required(ErrorMessage = "用戶密碼不能爲空!", AllowEmptyStrings = true)] [Display(Name = "密碼")] [MaxLength(9, ErrorMessage = "密碼最大爲 9 個字符!")] [MinLength(3, ErrorMessage = "密碼最小爲 3 個字符!")] [DataType(DataType.Password)] [RegularExpression("^(?:[0-9]+[a-zA-Z]|[a-zA-Z]+[0-9])[a-zA-Z0-9]*$", ErrorMessage = "密碼至少包含一個字母和數字!")] public string Password { get; set; } } }
頁面模型中,定義一個名爲 CurrentUser 的綁定屬性:
[BindProperty] public User CurrentUser { get; set; }
在頁面視圖中,將用戶名和密碼通過兩個文本輸入框渲染到頁面中:
<f:Window Width="350" WindowPosition="GoldenSection" EnableClose="false" IsModal="false" Title="登錄表單" ID="Window1"> <Items> <f:SimpleForm ShowHeader="false" BodyPadding="10" ShowBorder="false" ID="SimpleForm1"> <Items> <f:TextBox For="CurrentUser.UserName"></f:TextBox> @* <f:TextBox For="CurrentUser.Password"></f:TextBox> *@ </Items> </f:SimpleForm> </Items> <Toolbars> <f:Toolbar Position="Bottom" ToolbarAlign="Right" ID="Toolbar1"> <Items> <f:Button OnClick="btnLogin_Click" OnClickFields="SimpleForm1" ValidateTarget="Top" ValidateForms="SimpleForm1" Type="Submit" Text="登錄" ID="btnLogin"></f:Button> <f:Button Type="Reset" Text="重置" ID="btnReset"></f:Button> </Items> </f:Toolbar> </Toolbars> </f:Window>
注意,上述代碼中我們註釋掉了 CurrentUser.Password,以便在後臺驗證模型狀態驗證失敗的情況。
此時提交表單,FineUICore會自動彈出模型驗證失敗的消息,如下圖所示。
這個邏輯上是沒有問題的,那個彈出框提示是FineUICore系統處理的(無需用戶編碼),看下事件處理函數:
public IActionResult OnPostBtnLogin_Click() { if (ModelState.IsValid) { if (CurrentUser.UserName == "admin" && CurrentUser.Password == "admin888") { ShowNotify("成功登錄!", MessageBoxIcon.Success); } else { ShowNotify(String.Format("用戶名({0})或密碼({1})錯誤!", CurrentUser.UserName, CurrentUser.Password), MessageBoxIcon.Error); } } return UIHelper.Result(); }
爲了從模型驗證狀態中移除某些屬性,我們可以直接這麼寫:
public IActionResult OnPostBtnLogin_Click() { ModelState.Remove("CurrentUser.Password"); if (ModelState.IsValid) { ... } }
參考文章:https://stackoverflow.com/questions/16266988/exclude-fields-from-model-validation
上述文章指出,調用 ModelState.Remove() 方法雖然不夠優雅,但是可以快速解決問題。更加優雅的做法是自定義一個單獨的視圖模型,示例代碼:
public class PersonViewModel { [Required] public String FirstName { get; set; } [Required] public String LastName { get; set; } } public class PersonWithEmailViewModel : PersonViewModel { [Required] public String Email { get; set; } }