ASP.NET Core如何禁用模型驗證(或者從模型狀態中移除某些屬性)?

三生石上(FineUI控件)發表於2024-05-16

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

  

相關文章