這是一篇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; } }