Формы и проверка ASP.NET Core BlazorASP.NET Core Blazor forms and validation

Авторы: Дэниэл Рот (Daniel Roth) и Люк Лэтем (Luke Latham)By Daniel Roth and Luke Latham

Формы и проверка поддерживаются в Blazor с помощью заметок к данным.Forms and validation are supported in Blazor using data annotations.

Следующий тип ExampleModel определяет логику проверки с помощью заметок к данным:The following ExampleModel type defines validation logic using data annotations:

using System.ComponentModel.DataAnnotations;

public class ExampleModel
{
    [Required]
    [StringLength(10, ErrorMessage = "Name is too long.")]
    public string Name { get; set; }
}

Форма определяется с помощью компонента EditForm.A form is defined using the EditForm component. В следующей форме показаны типичные элементы, компоненты и код Razor:The following form demonstrates typical elements, components, and Razor code:

<EditForm Model="@_exampleModel" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <InputText id="name" @bind-Value="_exampleModel.Name" />

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

@code {
    private ExampleModel _exampleModel = new ExampleModel();

    private void HandleValidSubmit()
    {
        Console.WriteLine("OnValidSubmit");
    }
}

В предшествующем примере:In the preceding example:

  • Форма проверяет введенные пользователем данные в поле name, используя проверку, определенную в типе ExampleModel.The form validates user input in the name field using the validation defined in the ExampleModel type. Модель создается в блоке @code компонента и хранится в частном поле (_exampleModel).The model is created in the component's @code block and held in a private field (_exampleModel). Поле назначается атрибуту Model элемента <EditForm>.The field is assigned to the Model attribute of the <EditForm> element.
  • Элемент @bind-Value компонента InputText привязывает:The InputText component's @bind-Value binds:
    • Свойство модели (_exampleModel.Name) к свойству Value компонента InputText.The model property (_exampleModel.Name) to the InputText component's Value property.
    • Делегат события изменения к свойству ValueChanged компонента InputText.A change event delegate to the InputText component's ValueChanged property.
  • Компонент DataAnnotationsValidator привязывает поддержку проверки с помощью заметок к данным.The DataAnnotationsValidator component attaches validation support using data annotations.
  • Компонент ValidationSummary обобщает сообщения проверки.The ValidationSummary component summarizes validation messages.
  • HandleValidSubmit активируется, когда форма успешно отправляется (проходит проверку).HandleValidSubmit is triggered when the form successfully submits (passes validation).

Для получения и проверки вводимых пользователем данных доступны наборы встроенных компонентов ввода.A set of built-in input components are available to receive and validate user input. Входные данные проверяются при их изменении и отправке формы.Inputs are validated when they're changed and when a form is submitted. В приведенной ниже таблице показаны доступные компоненты ввода.Available input components are shown in the following table.

Компонент вводаInput component Отображается как …Rendered as…
InputText <input>
InputTextArea <textarea>
InputSelect <select>
InputNumber <input type="number">
InputCheckbox <input type="checkbox">
InputDate <input type="date">

Все компоненты ввода, включая EditForm, поддерживают произвольные атрибуты.All of the input components, including EditForm, support arbitrary attributes. В отрисованный HTML-элемент добавляется любой атрибут, который не соответствует параметру компонента.Any attribute that doesn't match a component parameter is added to the rendered HTML element.

Входные компоненты предоставляют поведение по умолчанию для проверки редактирования и изменения класса CSS в соответствии с состоянием поля.Input components provide default behavior for validating on edit and changing their CSS class to reflect the field state. Некоторые компоненты включают в себя полезную логику синтаксического анализа.Some components include useful parsing logic. Например, InputDate и InputNumber обрабатывают не анализируемые значения надлежащим образом, регистрируя их как ошибки проверки.For example, InputDate and InputNumber handle unparseable values gracefully by registering them as validation errors. Типы, которые могут принимать значения NULL, также поддерживают допустимость значений NULL для целевого поля (например, int?).Types that can accept null values also support nullability of the target field (for example, int?).

Следующий тип Starship определяет логику проверки, используя более широкий набор свойств и заметок к данным, чем предыдущий ExampleModel:The following Starship type defines validation logic using a larger set of properties and data annotations than the earlier ExampleModel:

using System;
using System.ComponentModel.DataAnnotations;

public class Starship
{
    [Required]
    [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
    public string Identifier { get; set; }

    public string Description { get; set; }

    [Required]
    public string Classification { get; set; }

    [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
    public int MaximumAccommodation { get; set; }

    [Required]
    [Range(typeof(bool), "true", "true", 
        ErrorMessage = "This form disallows unapproved ships.")]
    public bool IsValidatedDesign { get; set; }

    [Required]
    public DateTime ProductionDate { get; set; }
}

В предыдущем примере Description является необязательным, так как заметки к данным отсутствуют.In the preceding example, Description is optional because no data annotations are present.

Следующая форма проверяет введенные пользователем данные, используя проверку, определенную в модели Starship:The following form validates user input using the validation defined in the Starship model:

@page "/FormsValidation"

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="@_starship" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <p>
        <label>
            Identifier:
            <InputText @bind-Value="_starship.Identifier" />
        </label>
    </p>
    <p>
        <label>
            Description (optional):
            <InputTextArea @bind-Value="_starship.Description" />
        </label>
    </p>
    <p>
        <label>
            Primary Classification:
            <InputSelect @bind-Value="_starship.Classification">
                <option value="">Select classification ...</option>
                <option value="Exploration">Exploration</option>
                <option value="Diplomacy">Diplomacy</option>
                <option value="Defense">Defense</option>
            </InputSelect>
        </label>
    </p>
    <p>
        <label>
            Maximum Accommodation:
            <InputNumber @bind-Value="_starship.MaximumAccommodation" />
        </label>
    </p>
    <p>
        <label>
            Engineering Approval:
            <InputCheckbox @bind-Value="_starship.IsValidatedDesign" />
        </label>
    </p>
    <p>
        <label>
            Production Date:
            <InputDate @bind-Value="_starship.ProductionDate" />
        </label>
    </p>

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

    <p>
        <a href="http://www.startrek.com/">Star Trek</a>, 
        &copy;1966-2019 CBS Studios, Inc. and 
        <a href="https://www.paramount.com">Paramount Pictures</a>
    </p>
</EditForm>

@code {
    private Starship _starship = new Starship();

    private void HandleValidSubmit()
    {
        Console.WriteLine("OnValidSubmit");
    }
}

EditForm создает EditContext в виде каскадного значения, которое отслеживает метаданные процесса редактирования, включая измененные поля и текущие сообщения проверки.The EditForm creates an EditContext as a cascading value that tracks metadata about the edit process, including which fields have been modified and the current validation messages. EditForm также предоставляет удобные события для допустимых и недопустимых отправок (OnValidSubmit, OnInvalidSubmit).The EditForm also provides convenient events for valid and invalid submits (OnValidSubmit, OnInvalidSubmit). Кроме того, можно использовать OnSubmit, чтобы активировать проверку полей значений с помощью пользовательского кода проверки.Alternatively, use OnSubmit to trigger the validation and check field values with custom validation code.

В следующем примере:In the following example:

  • Метод HandleSubmit выполняется при нажатии кнопки Отправить.The HandleSubmit method runs when the Submit button is selected.
  • Форма проверяется с помощью EditContext формы.The form is validated using the form's EditContext.
  • Затем форма проверяется с помощью передачи EditContext методу ServerValidate, который вызывает конечную точку веб-API на сервере (не отображается).The form is further validated by passing the EditContext to the ServerValidate method that calls a web API endpoint on the server (not shown).
  • Дополнительный код выполняется в зависимости от результатов проверки на стороне клиента и сервера путем проверки isValid.Additional code is run depending on the result of the client- and server-side validation by checking isValid.
<EditForm EditContext="@_editContext" OnSubmit="@HandleSubmit">

    ...

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

@code {
    private Starship _starship = new Starship();
    private EditContext _editContext;

    protected override void OnInitialized()
    {
        _editContext = new EditContext(_starship);
    }

    private async Task HandleSubmit()
    {
        var isValid = _editContext.Validate() && 
            await ServerValidate(_editContext);

        if (isValid)
        {
            ...
        }
        else
        {
            ...
        }
    }

    private async Task<bool> ServerValidate(EditContext editContext)
    {
        var serverChecksValid = ...

        return serverChecksValid;
    }
}

InputText на основе события вводаInputText based on the input event

Используйте компонент InputText, чтобы создать пользовательский компонент, использующий событие input, а не событие change.Use the InputText component to create a custom component that uses the input event instead of the change event.

Создайте компонент со следующей разметкой и используйте компонент так же, как используется InputText:Create a component with the following markup, and use the component just as InputText is used:

@inherits InputText

<input 
    @attributes="AdditionalAttributes" 
    class="@CssClass" 
    value="@CurrentValue" 
    @oninput="EventCallback.Factory.CreateBinder<string>(
        this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />

Работа с переключателямиWork with radio buttons

При работе с переключателями в форме привязка данных обрабатывается иначе, чем другие элементы, так как переключатели оцениваются как группа.When working with radio buttons in a form, data binding is handled differently than other elements because radio buttons are evaluated as a group. Значение каждого переключателя является фиксированным, но значение группы переключателей является значением выбранного переключателя.The value of each radio button is fixed, but the value of the radio button group is the value of the selected radio button. В приведенном ниже примере показано, как выполнить следующие задачи.The following example shows how to:

  • Обработка привязки данных для группы переключателей.Handle data binding for a radio button group.
  • Поддержка проверки с помощью пользовательского компонента InputRadio.Support validation using a custom InputRadio component.
@using System.Globalization
@typeparam TValue
@inherits InputBase<TValue>

<input @attributes="AdditionalAttributes" type="radio" value="@SelectedValue" 
       checked="@(SelectedValue.Equals(Value))" @onchange="OnChange" />

@code {
    [Parameter]
    public TValue SelectedValue { get; set; }

    private void OnChange(ChangeEventArgs args)
    {
        CurrentValueAsString = args.Value.ToString();
    }

    protected override bool TryParseValueFromString(string value, 
        out TValue result, out string errorMessage)
    {
        var success = BindConverter.TryConvertTo<TValue>(
            value, CultureInfo.CurrentCulture, out var parsedValue);
        if (success)
        {
            result = parsedValue;
            errorMessage = null;

            return true;
        }
        else
        {
            result = default;
            errorMessage = $"{FieldIdentifier.FieldName} field isn't valid.";

            return false;
        }
    }
}

Следующий EditForm использует предыдущий компонент InputRadio для получения и проверки оценки пользователя:The following EditForm uses the preceding InputRadio component to obtain and validate a rating from the user:

@page "/RadioButtonExample"
@using System.ComponentModel.DataAnnotations

<h1>Radio Button Group Test</h1>

<EditForm Model="_model" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    @for (int i = 1; i <= 5; i++)
    {
        <label>
            <InputRadio name="rate" SelectedValue="i" @bind-Value="_model.Rating" />
            @i
        </label>
    }

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

<p>You chose: @_model.Rating</p>

@code {
    private Model _model = new Model();

    private void HandleValidSubmit()
    {
        Console.WriteLine("valid");
    }

    public class Model
    {
        [Range(1, 5)]
        public int Rating { get; set; }
    }
}

Поддержка проверкиValidation support

Компонент DataAnnotationsValidator привязывает поддержку проверки с помощью заметок к данным к каскадному EditContext.The DataAnnotationsValidator component attaches validation support using data annotations to the cascaded EditContext. Чтобы включить поддержку проверки с помощью заметок к данным, требуется это явное действие.Enabling support for validation using data annotations requires this explicit gesture. Чтобы использовать другую систему проверки, а не заметки к данным, замените DataAnnotationsValidator пользовательской реализацией.To use a different validation system than data annotations, replace the DataAnnotationsValidator with a custom implementation. Реализация ASP.NET Core доступна для проверки в эталонном источнике: DataAnnotationsValidator/AddDataAnnotationsValidation.The ASP.NET Core implementation is available for inspection in the reference source: DataAnnotationsValidator/AddDataAnnotationsValidation.

Blazor выполняет два типа проверки данных: performs two types of validation:

  • Проверка поля выполняется, когда пользователь выходит за пределы поля.Field validation is performed when the user tabs out of a field. Во время проверки поля компонент DataAnnotationsValidator связывает все результаты проверки с полем.During field validation, the DataAnnotationsValidator component associates all reported validation results with the field.
  • Проверка модели выполняется, когда пользователь отправляет форму.Model validation is performed when the user submits the form. Во время проверки модели компонент DataAnnotationsValidator пытается определить поле на основе имени члена из результатов проверки.During model validation, the DataAnnotationsValidator component attempts to determine the field based on the member name that the validation result reports. Результаты проверки, не связанные с отдельным элементом, связаны с моделью, а не с полем.Validation results that aren't associated with an individual member are associated with the model rather than a field.

Сводка проверки и компоненты сообщений о проверкеValidation Summary and Validation Message components

Компонент ValidationSummary содержит обзор всех сообщений проверки аналогично вспомогательному методу тега сводки проверки:The ValidationSummary component summarizes all validation messages, which is similar to the Validation Summary Tag Helper:

<ValidationSummary />

Выходные сообщения проверки для конкретной модели с параметром Model:Output validation messages for a specific model with the Model parameter:

<ValidationSummary Model="@_starship" />

Компонент ValidationMessage отображает сообщения проверки для определенного поля, которые похожи на вспомогательный метод тега сообщения проверки.The ValidationMessage component displays validation messages for a specific field, which is similar to the Validation Message Tag Helper. Укажите поле для проверки с помощью атрибута For и лямбда-выражения с именем свойства модели:Specify the field for validation with the For attribute and a lambda expression naming the model property:

<ValidationMessage For="@(() => _starship.MaximumAccommodation)" />

Компоненты ValidationMessage и ValidationSummary поддерживают произвольные атрибуты.The ValidationMessage and ValidationSummary components support arbitrary attributes. В созданный элемент <div> или <ul> добавляется любой атрибут, который не соответствует параметру компонента.Any attribute that doesn't match a component parameter is added to the generated <div> or <ul> element.

Пользовательские атрибуты проверкиCustom validation attributes

Чтобы убедиться, что результат проверки правильно связан с полем при использовании настраиваемого атрибута проверки, передайте MemberName контекста проверки при создании ValidationResult:To ensure that a validation result is correctly associated with a field when using a custom validation attribute, pass the validation context's MemberName when creating the ValidationResult:

using System;
using System.ComponentModel.DataAnnotations;

private class MyCustomValidator : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, 
        ValidationContext validationContext)
    {
        ...

        return new ValidationResult("Validation message to user.",
            new[] { validationContext.MemberName });
    }
}

Пакет проверки заметок к данным в BlazorBlazor data annotations validation package

Microsoft.AspNetCore.Components.DataAnnotations.Validation — это пакет, который заполняет пропуски проверки с помощью компонента DataAnnotationsValidator.The Microsoft.AspNetCore.Components.DataAnnotations.Validation is a package that fills validation experience gaps using the DataAnnotationsValidator component. В настоящее время пакет является экспериментальным.The package is currently experimental.

Атрибут [CompareProperty][CompareProperty] attribute

CompareAttribute плохо работает с компонентом DataAnnotationsValidator, так как он не связывает результат проверки с конкретным элементом.The CompareAttribute doesn't work well with the DataAnnotationsValidator component because it doesn't associate the validation result with a specific member. Это может привести к несогласованному поведению при проверке на уровне полей и при проверке всей модели при отправке.This can result in inconsistent behavior between field-level validation and when the entire model is validated on a submit. Экспериментальный пакет Microsoft.AspNetCore.Components.DataAnnotations.Validation содержит дополнительный атрибут проверки ComparePropertyAttribute, который обходит эти ограничения.The Microsoft.AspNetCore.Components.DataAnnotations.Validation experimental package introduces an additional validation attribute, ComparePropertyAttribute, that works around these limitations. В приложении Blazor [CompareProperty] является непосредственной заменой атрибута [Compare].In a Blazor app, [CompareProperty] is a direct replacement for the [Compare] attribute.

Вложенные модели, типы коллекций и сложные типыNested models, collection types, and complex types

Blazor обеспечивает поддержку проверки входных данных формы с помощью заметок к данным со встроенным DataAnnotationsValidator. provides support for validating form input using data annotations with the built-in DataAnnotationsValidator. Однако DataAnnotationsValidator проверяет только свойства верхнего уровня модели, привязанной к форме, которые не являются свойствами типа коллекции или сложного типа.However, the DataAnnotationsValidator only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.

Чтобы проверить весь граф объектов привязанной модели, включая свойства типа коллекции и сложного типа, используйте ObjectGraphDataAnnotationsValidator, предоставляемый экспериментальным пакетом Microsoft.AspNetCore.Components.DataAnnotations.Validation:To validate the bound model's entire object graph, including collection- and complex-type properties, use the ObjectGraphDataAnnotationsValidator provided by the experimental Microsoft.AspNetCore.Components.DataAnnotations.Validation package:

<EditForm Model="@_model" OnValidSubmit="HandleValidSubmit">
    <ObjectGraphDataAnnotationsValidator />
    ...
</EditForm>

Делайте заметки для свойств модели с помощью [ValidateComplexType].Annotate model properties with [ValidateComplexType]. В следующих классах модели класс ShipDescription содержит дополнительные заметки к данным для проверки, когда модель привязана к форме:In the following model classes, the ShipDescription class contains additional data annotations to validate when the model is bound to the form:

Starship.cs:Starship.cs:

using System;
using System.ComponentModel.DataAnnotations;

public class Starship
{
    ...

    [ValidateComplexType]
    public ShipDescription ShipDescription { get; set; }

    ...
}

ShipDescription.cs:ShipDescription.cs:

using System;
using System.ComponentModel.DataAnnotations;

public class ShipDescription
{
    [Required]
    [StringLength(40, ErrorMessage = "Description too long (40 char).")]
    public string ShortDescription { get; set; }
    
    [Required]
    [StringLength(240, ErrorMessage = "Description too long (240 char).")]
    public string LongDescription { get; set; }
}

Предоставление кнопки "Отправить" на основе проверки формыEnable the submit button based on form validation

Чтобы включить или отключить кнопку "Отправить" на основе проверки формы, выполните следующие действия:To enable and disable the submit button based on form validation:

  • Используйте EditContext формы, чтобы назначить модель при инициализации компонента.Use the form's EditContext to assign the model when the component is initialized.
  • Проверьте форму в обратном вызове OnFieldChanged контекста, чтобы включить и отключить кнопку "Отправить".Validate the form in the context's OnFieldChanged callback to enable and disable the submit button.
  • Отсоедините обработчик событий в методе Dispose.Unhook the event handler in the Dispose method. Для получения дополнительной информации см. Жизненный цикл ASP.NET Core Blazor.For more information, see Жизненный цикл ASP.NET Core Blazor.
@implements IDisposable

<EditForm EditContext="@_editContext">
    <DataAnnotationsValidator />
    <ValidationSummary />

    ...

    <button type="submit" disabled="@_formInvalid">Submit</button>
</EditForm>

@code {
    private Starship _starship = new Starship();
    private bool _formInvalid = true;
    private EditContext _editContext;

    protected override void OnInitialized()
    {
        _editContext = new EditContext(_starship);
        _editContext.OnFieldChanged += HandleFieldChanged;
    }

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        _formInvalid = !_editContext.Validate();
        StateHasChanged();
    }

    public void Dispose()
    {
        _editContext.OnFieldChanged -= HandleFieldChanged;
    }
}

В примерах выше установите для параметра _formInvalid значение false, если:In the preceding example, set _formInvalid to false if:

  • Форма предварительно загружена с допустимыми значениями по умолчанию.The form is preloaded with valid default values.
  • Вы хотите, чтобы кнопка отправки была включена при загрузке формы.You want the submit button enabled when the form loads.

Побочным эффектом предыдущего подхода является то, что компонент ValidationSummary заполняется недопустимыми полями после того, как пользователь взаимодействует с одним полем.A side effect of the preceding approach is that a ValidationSummary component is populated with invalid fields after the user interacts with any one field. Этот сценарий можно решить одним из следующих способов:This scenario can be addressed in either of the following ways:

  • Не используйте компонент ValidationSummary в форме.Don't use a ValidationSummary component on the form.
  • Сделайте компонент ValidationSummary видимым при нажатии кнопки "Отправить" (например, в методе HandleValidSubmit).Make the ValidationSummary component visible when the submit button is selected (for example, in a HandleValidSubmit method).
<EditForm EditContext="@_editContext" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary style="@_displaySummary" />

    ...

    <button type="submit" disabled="@_formInvalid">Submit</button>
</EditForm>

@code {
    private string _displaySummary = "display:none";

    ...

    private void HandleValidSubmit()
    {
        _displaySummary = "display:block";
    }
}