ASP.NET Core Blazor 表單驗證

注意

這不是這篇文章的最新版本。 關於目前版本,請參閱本文的 .NET 10 版本。

警告

此版本的 ASP.NET Core 已不再支援。 欲了解更多資訊,請參閱.NET及.NET核心支援政策。 關於目前版本,請參閱本文的 .NET 10 版本。

本文說明如何在表單中驗證使用者輸入 Blazor 。

對大多數表單來說,最簡單且推薦的方法是為模型新增資料註解、驗證屬性,並在 中放置DataAnnotationsValidator一個元件。EditForm Blazor 也支援透過表單 EditContext的自訂驗證,無論是直接在表單元件中,或是可重用的驗證器元件中進行。

相關文章提供更多細節:

用資料註解驗證

以下模型使用 RequiredAttribute 與 RangeAttribute:

Starship.cs:

using System.ComponentModel.DataAnnotations;

public class Starship
{
    [Required]
    public string? Identifier { get; set; }

    [Range(1, 10, ErrorMessage = "Accommodation must be between 1 and 10.")]
    public int MaximumAccommodation { get; set; }
}

將模型新增至 EditForm、包含 DataAnnotationsValidator,並使用 ValidationMessage<TValue> 或 ValidationSummary 顯示錯誤。 OnValidSubmit回調僅在驗證成功時被呼叫:

<EditForm Model="Model" OnValidSubmit="Submit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <p>
        <label>
            Identifier:
            <InputText @bind-Value="Model.Identifier" />
        </label>
        <ValidationMessage For="() => Model.Identifier" />
    </p>

    <p>
        <label>
            Maximum accommodation:
            <InputNumber @bind-Value="Model.MaximumAccommodation" />
        </label>
        <ValidationMessage For="() => Model.MaximumAccommodation" />
    </p>

    <button type="submit">Submit</button>
</EditForm>

@code {
    private Starship Model { get; } = new Starship();

    private void Submit()
    {
        // Process the valid form.
    }
}

對於靜態 SSR 表單貼文,指派一個唯一 FormName 並收到已發布的模型,格式為 [SupplyParameterFromForm]:

<EditForm Model="Model" FormName="starship" OnValidSubmit="Submit">
    ...
</EditForm>

@code {
    [SupplyParameterFromForm]
    private Starship? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();
}

如需更多關於在各種轉譯模式下的表單提交與模型繫結的資訊,請參閱 ASP.NET Core Blazor 表單概觀 和 ASP.NET Core Blazor 表單繫結。

若沒有 DataAnnotationsValidator 元件,模型上的驗證屬性不會參與表單的驗證。

執行驗證時

Blazor 執行欄位驗證與完整形式驗證:

  • 欄位驗證會在欄位變更後執行。 在互動式表單中,使用者編輯表單時,這會發生在 .NET 檔案中。
  • 完整形式驗證通常在 EditForm 處理透過 OnValidSubmit 或 OnInvalidSubmit的提交時執行。 OnSubmit處理器會控制驗證,如控制表單提交中所述。

靜態 SSR 表單可在靜態 SSR 中提供即時的瀏覽器回饋,並支援 ASP.NET Core Blazor 用戶端表單驗證。 表單發布後會在伺服器上再次權威驗證。

靜態的 SSR 表單會在伺服器上被驗證,且不會在請求間提供即時欄位驗證。

識別成員的驗證結果會與該欄位相關聯。 沒有成員名稱的結果會與模型相關聯,並出現在驗證摘要中,而非欄位的 ValidationMessage 組件。

配置資料註解驗證

DataAnnotationsValidator 一律為表單啟用 DataAnnotations 驗證。 若要使用 Microsoft.Extensions.Validation 套件所提供的擴充驗證功能,請在 AddValidation 檔案中呼叫 Program 擴充方法:

builder.Services.AddValidation();

AddValidation 呼叫會註冊套件的驗證服務,並啟動原始碼產生器,為偵測到的模型類型建立驗證中繼資料。 可用的行為取決於該中繼資料是否包含表單模型:

Configuration 行為
已產生的元資料可供使用。 驗證巢狀物件與集合,並支援訊息本地化。
產生的元資料無法提供 它會驗證頂層屬性,但不驗證巢狀物件或集合,也不使用 Microsoft.Extensions.Validation 訊息本地化管線。
Configuration 行為
已產生的元資料可供使用。 驗證巢狀物件與集合。
產生的元資料無法提供 僅驗證頂層屬性。

在 .NET 10 中,這些 ValidatableTypeAttributeSkipValidationAttribute API 仍處於實驗階段。 欲了解詳情及可用變通方法,請參閱 ASP.NET Core 中的驗證。

使用 Microsoft.Extensions.Validation時,請以 C# 檔案(.cs)宣告模型類型,而非 Razor 元件檔案(.razor)。 原始碼產生器會從 C# 原始碼產生驗證元資料,且無法包含元件中 Razor 宣告的模型類型。

關於組態需求、驗證順序、自訂規則、巢狀物件圖及產生的元資料,請參見 ASP.NET Core 中的驗證。

驗證巢狀物件圖

在 .NET 9 或更早版本中,會DataAnnotationsValidator驗證頂層模型屬性,但不會遞迴驗證集合型或複雜型態屬性。 對於遞迴驗證,請使用實驗性 套件中的 和 :

<EditForm Model="Model" OnValidSubmit="Submit">
    <ObjectGraphDataAnnotationsValidator />
    ...
</EditForm>
public class Starship
{
    [ValidateComplexType]
    public ShipDescription Description { get; set; } =
        new ShipDescription();
}

該套件在這些框架版本中仍處於實驗性質。

[CompareProperty] 屬性

對於 .NET 5 或更早版本,請使用 experimental 套件的 ComparePropertyAttribute,而非 CompareAttribute。 ComparePropertyAttribute 在欄位驗證和整份表單驗證期間,都會一致地將驗證結果與欄位關聯起來。

撰寫基於模型的自訂規則

當內建屬性無法表達規則時,請使用自訂 ValidationAttribute 或 IValidatableObject。 詳細指引請參閱 ASP.NET Core 中的驗證。

撰寫基於模型的自訂規則

當內建屬性無法表達規則時,請使用 自訂的驗證屬性 或實作 IValidatableObject。 兩者都貫穿 DataAnnotationsValidator。

從自訂屬性回傳 a ValidationResult 時,請包含已驗證的成員名稱,讓結果能顯示在該欄位的 ValidationMessage 元件中。

自訂屬性可透過 GetService 解析已註冊的服務。

透過 EditContext 新增驗證

EditForm 會在其 EditContext 參數被指派時自動建立 Model。 若要直接使用驗證 API,請自行建立上下文並將其指派到 EditContext 參數 EditForm。 不要將 Model 和 EditContext 同時指派給同一個表單。

自訂驗證通常會使用:

以下互動式表單模式在資料註解驗證外新增表單層級商業規則,並在任一相關欄位變更時重新檢查規則:

@implements IDisposable

<EditForm EditContext="editContext" OnValidSubmit="Submit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    ...
</EditForm>

@code {
    private Starship Model { get; } = new Starship();
    private EditContext editContext = default!;
    private ValidationMessageStore messages = default!;

    protected override void OnInitialized()
    {
        editContext = new EditContext(Model);
        messages = new ValidationMessageStore(editContext);
        editContext.OnValidationRequested += ValidateBusinessRules;
        editContext.OnFieldChanged += ValidateChangedField;
    }

    private void ValidateBusinessRules(
        object? sender, ValidationRequestedEventArgs e)
    {
        messages.Clear();
        ValidateIdentifier();
        editContext.NotifyValidationStateChanged();
    }

    private void ValidateChangedField(
        object? sender, FieldChangedEventArgs e)
    {
        if (e.FieldIdentifier.FieldName != nameof(Starship.Identifier) &&
            e.FieldIdentifier.FieldName != nameof(Starship.MaximumAccommodation))
        {
            return;
        }

        messages.Clear(
            editContext.Field(nameof(Starship.Identifier)));
        ValidateIdentifier();
        editContext.NotifyValidationStateChanged();
    }

    private void ValidateIdentifier()
    {
        if (Model.MaximumAccommodation == 1 &&
            string.IsNullOrWhiteSpace(Model.Identifier))
        {
            messages.Add(
                editContext.Field(nameof(Starship.Identifier)),
                "An identifier is required for a single-occupant ship.");
        }
    }

    private void Submit()
    {
        // Process the valid form.
    }

    public void Dispose()
    {
        editContext.OnValidationRequested -= ValidateBusinessRules;
        editContext.OnFieldChanged -= ValidateChangedField;
    }
}

OnFieldChanged 處理常式會在 e.FieldIdentifier 中接收已變更的欄位。 清除或替換受影響的訊息並呼叫 NotifyValidationStateChanged,如前例所示。

靜態 SSR 不會在請求間提供即時的 .NET 欄位驗證。

若要進行非同步的整個表單驗證,請從 e.AddAsyncValidator 處理常式呼叫 OnValidationRequested。 若要以互動式形式進行非同步欄位驗證,請從OnFieldChanged處理器呼叫EditContext.RegisterAsyncFieldValidator。 同一欄位的新非同步驗證會取代並取消先前驗證。

關於基於模型的非同步驗證屬性,請參見 ASP.NET Core 中的驗證。 欲了解完整的可重複使用驗證器元件,請參見 ASP.NET Core Blazor 進階表單驗證。

若想了解一個可重複使用的實作,能封裝事件訂閱及其訊息庫,請參見 ASP.NET Core Blazor 進階表單驗證。

顯示驗證訊息

用於 ValidationMessage<TValue> 顯示與某一欄位相關的訊息:

<ValidationMessage For="() => Model.Identifier" />

此 For 表達式透過其模型實例與屬性來識別該欄位。 Blazor 會使用 FieldIdentifier 來表示此識別,因此,不同模型執行個體中名稱相同的屬性會被視為不同欄位。 使用表達式取代字串也讓重構工具能更新屬性參考。

用於 ValidationSummary 顯示以下表單的訊息:

<ValidationSummary />

將摘要 Model 參數指定為僅限於特定模型相關的訊息:

<ValidationSummary Model="Model" />

若要檢查程式碼中的當前訊息,請呼叫 GetValidationMessages:

var allMessages = editContext.GetValidationMessages();
var fieldMessages = editContext.GetValidationMessages(
    editContext.Field(nameof(Starship.Identifier)));

這些方法讀取當前的驗證狀態。 他們不會主動提出認同。

自訂驗證外觀

Blazor 應用代表欄位與訊息狀態的 CSS 類別:

Element 班級
Input valid 或 invalid,加上 modified 使用者編輯欄位後
驗證訊息 validation-message
驗證摘要 validation-summary-errors 或 validation-summary-valid

非同步欄位驗證的輸入使用 pending 或 faulted,可選地使用 , modified代替 valid 或 invalid ,且對應狀態適用。

Blazor專案範本包含了通用且有效與無效類別的樣式。 視需要為其他類別新增樣式。 ValidationMessage 同時 ValidationSummary 也接受任意的 HTML 屬性。 提供 class 屬性會取代元件的預設類別。

要改變應用於輸入元件的類別,請從 推導出 FieldCssClassProvider。

using Microsoft.AspNetCore.Components.Forms;

public sealed class BootstrapFieldCssClassProvider : FieldCssClassProvider
{
    public override string GetFieldCssClass(
        EditContext editContext,
        in FieldIdentifier fieldIdentifier)
    {
        if (!editContext.IsModified(fieldIdentifier))
        {
            return string.Empty;
        }

        return editContext.IsValid(fieldIdentifier)
            ? "is-valid"
            : "is-invalid";
    }
}
using System.Linq;
using Microsoft.AspNetCore.Components.Forms;

public sealed class BootstrapFieldCssClassProvider : FieldCssClassProvider
{
    public override string GetFieldCssClass(
        EditContext editContext,
        in FieldIdentifier fieldIdentifier)
    {
        if (!editContext.IsModified(fieldIdentifier))
        {
            return string.Empty;
        }

        return editContext.GetValidationMessages(fieldIdentifier).Any()
            ? "is-invalid"
            : "is-valid";
    }
}

自訂系統 FieldCssClassProvider 會決定每個欄位的完整類別值。 若表單使用非同步欄位驗證,則在需要 pending 或 faulted 類別時,於提供者中處理 IsValidationPending(fieldIdentifier) 和 IsValidationFaulted(fieldIdentifier)。

將提供者指派至表單:EditContext

editContext.SetFieldCssClassProvider(
    new BootstrapFieldCssClassProvider());

若要自訂輸入標記,請呼叫 FieldCssClass 以取得目前提供者所選的類別。

當為 EditForm 指派模型時,其子內容會接收產生的 EditContext。 透過參數 Context 捕捉上下文,並呼叫 FieldCssClass 將欄位的類別套用到周圍的標記:

<EditForm Model="Model" Context="editContext">
    <DataAnnotationsValidator />

    <div class="@editContext.FieldCssClass(
        () => Model.Identifier)">
        <InputText @bind-Value="Model.Identifier" />
        <ValidationMessage For="() => Model.Identifier" />
    </div>
</EditForm>

回應驗證狀態

EditContext 在不啟動驗證的情況下,暴露目前的驗證狀態。

  • 使用 IsModified(field) 或 IsModified() 來判斷表單中某欄位或任何欄位是否已變更。
  • 使用 GetValidationMessages(field) 或 GetValidationMessages() 檢查當前欄位或格式訊息。

用 IsValid(field) 來判斷欄位目前是否有驗證訊息。

對於欄位,可使用 !editContext.GetValidationMessages(field).Any() 檢查是否沒有訊息。

以下範例僅在欄位被修改且無效後才顯示自訂使用者介面:

@{
    var identifier = editContext.Field(nameof(Starship.Identifier));
}

@if (editContext.IsModified(identifier) &&
    !editContext.IsValid(identifier))
{
    <p>Correct the identifier before continuing.</p>
}
@{
    var identifier = editContext.Field(nameof(Starship.Identifier));
}

@if (editContext.IsModified(identifier) &&
    editContext.GetValidationMessages(identifier).Any())
{
    <p>Correct the identifier before continuing.</p>
}

輸入元件、ValidationMessage 和 ValidationSummary 會在驗證狀態變更時自行更新。 渲染其他條件驗證 UI 的元件應訂閱 OnValidationStateChanged 並呼叫 StateHasChanged:

private void HandleValidationStateChanged(
    object? sender, ValidationStateChangedEventArgs e) =>
    _ = InvokeAsync(StateHasChanged);

當元件遭到處置時,取消訂閱 OnValidationStateChanged。

使用 IsValidationPending(field) 與 IsValidationFaulted(field) 用於非同步欄位驗證。 不含參數的方法描述表單層級的 ValidateAsync 傳遞,且不會彙總每個欄位的狀態。

這些狀態也包括由 DataAnnotationsValidator執行的非同步工作。 套用至屬性的 AsyncValidationAttribute 會在欄位驗證期間使用欄位狀態,包括預設的 pending 和 faulted CSS 類別。 在 ValidateAsync期間,非同步屬性 和 IAsyncValidatableObject 對無參數方法報告的形式層級狀態有貢獻。

待處理的即時指示器需要互動式渲染模式。 在靜態 SSR 表單發佈時,伺服器端驗證會在回應呈現前完成。

控制表單提交

EditForm 提供三種提交回呼:

Callback 行為
OnValidSubmit 在自動驗證成功後執行。
OnInvalidSubmit 自動驗證失敗後執行。
OnSubmit 讓處理程式控制驗證和提交。

OnValidSubmit 且 OnInvalidSubmit 可同時使用。 不要將 OnSubmit 與它們其中任何一個結合。

EditForm在呼叫 OnValidSubmit 或 OnInvalidSubmit之前使用ValidateAsync,因此等待同步與非同步驗證者。 處理 OnSubmit 時,請先呼叫 ValidateAsync,再處理表單:

<EditForm EditContext="editContext" OnSubmit="HandleSubmit">
    ...
</EditForm>

@code {
    private async Task HandleSubmit(EditContext editContext)
    {
        if (await editContext.ValidateAsync())
        {
            await SaveAsync();
        }
    }
}

Validate同步方法在 .NET 11 中已過時。 它不會等待非同步驗證,且如果處理常式嘗試註冊非同步工作,便會擲回錯誤。

對於互動式表單,可使用表單層級的待處理狀態,在 ValidateAsync 執行期間停用提交功能:

<button type="submit" disabled="@editContext.IsValidationPending()">
    Save
</button>

處理 OnSubmit 時,請先呼叫 Validate,再處理表單:

<EditForm EditContext="editContext" OnSubmit="HandleSubmit">
    ...
</EditForm>

@code {
    private void HandleSubmit(EditContext editContext)
    {
        if (editContext.Validate())
        {
            Save();
        }
    }
}

其他資源