Проверка модели в ASP.NET Core MVC и Razor PagesModel validation in ASP.NET Core MVC and Razor Pages

Автор: Кирк Ларкин (Kirk Larkin)By Kirk Larkin

В этой статье объясняется, как осуществлять проверки вводимых пользователем данных в приложении ASP.NET Core MVC или Razor Pages.This article explains how to validate user input in an ASP.NET Core MVC or Razor Pages app.

Просмотреть или скачать пример кода (описание скачивания).View or download sample code (how to download).

Состояние моделиModel state

Состояние модели представляет ошибки, создаваемые двумя подсистемами: привязкой модели и проверкой модели.Model state represents errors that come from two subsystems: model binding and model validation. Ошибки привязки модели обычно являются ошибками преобразования данных.Errors that originate from model binding are generally data conversion errors. Например, в целочисленном поле указывается "x".For example, an "x" is entered in an integer field. Проверка модели происходит после ее привязки. В процессе сообщается об ошибках несоответствия данных бизнес-правилам.Model validation occurs after model binding and reports errors where data doesn't conform to business rules. Например, в поле, которое ожидает оценку от 1 до 5, указывается 0.For example, a 0 is entered in a field that expects a rating between 1 and 5.

Привязка и проверка модели происходят перед выполнением действия контроллера или метода обработчика Razor Pages.Both model binding and model validation occur before the execution of a controller action or a Razor Pages handler method. Веб-приложение отвечает за проверку ModelState.IsValid и реагирует соответствующим образом.For web apps, it's the app's responsibility to inspect ModelState.IsValid and react appropriately. Веб-приложения обычно повторно отображают страницы с сообщением об ошибке.Web apps typically redisplay the page with an error message:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Контроллерам веб-API не нужно проверять ModelState.IsValid при наличии атрибута [ApiController].Web API controllers don't have to check ModelState.IsValid if they have the [ApiController] attribute. В этом случае автоматически возвращается ответ HTTP 400, содержащий сведения об ошибке, если состояние модели недопустимо.In that case, an automatic HTTP 400 response containing error details is returned when model state is invalid. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400.For more information, see Automatic HTTP 400 responses.

Перезапуск проверкиRerun validation

Проверка выполняется автоматически, однако может потребоваться повторить ее вручную.Validation is automatic, but you might want to repeat it manually. Например, можно вычислить значение свойства и повторно выполнить проверку после установки свойства в вычисляемое значение.For example, you might compute a value for a property and want to rerun validation after setting the property to the computed value. Для повторной проверки вызовите метод TryValidateModel, как показано ниже.To rerun validation, call the TryValidateModel method, as shown here:

Movie.ReleaseDate = modifiedReleaseDate;

if (!TryValidateModel(Movie, nameof(Movie)))
{
    return Page();
}

_context.Movies.Add(Movie);
await _context.SaveChangesAsync();

return RedirectToPage("./Index");

Атрибуты проверкиValidation attributes

Атрибуты проверки позволяют задать правила проверки для свойств модели.Validation attributes let you specify validation rules for model properties. В следующем примере из примера приложения показан класс модели, который помечается с помощью атрибутов проверки.The following example from the sample app shows a model class that is annotated with validation attributes. [ClassicMovie] является настраиваемым атрибутом проверки; остальные являются встроенными.The [ClassicMovie] attribute is a custom validation attribute and the others are built-in. Не показан [ClassicMovieWithClientValidator].Not shown is [ClassicMovieWithClientValidator]. [ClassicMovieWithClientValidator] демонстрирует альтернативный способ реализации настраиваемого атрибута.[ClassicMovieWithClientValidator] shows an alternative way to implement a custom attribute.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Встроенные атрибутыBuilt-in attributes

Ниже приведены некоторые из встроенных атрибутов проверки.Here are some of the built-in validation attributes:

  • [CreditCard]. проверяет, имеет ли свойство формат кредитной карты.[CreditCard]: Validates that the property has a credit card format.
  • [Compare]. проверяет, совпадают ли два свойства модели.[Compare]: Validates that two properties in a model match.
  • [EmailAddress]. проверяет, имеет ли свойство формат адреса электронной почты.[EmailAddress]: Validates that the property has an email format.
  • [Phone]. проверяет, имеет ли свойство формат номера телефона.[Phone]: Validates that the property has a telephone number format.
  • [Range]. проверяет, находится ли значение свойства в указанном диапазоне.[Range]: Validates that the property value falls within a specified range.
  • [RegularExpression]. проверяет, соответствует ли значение свойства указанному регулярному выражению.[RegularExpression]: Validates that the property value matches a specified regular expression.
  • [Required]. проверяет, что поле не равно NULL.[Required]: Validates that the field is not null. Дополнительные сведения о поведении этого атрибута см. в разделе Атрибут [Required].See [Required] attribute for details about this attribute's behavior.
  • [StringLength]. проверяет, что значение свойства строки не превышает ограничение по указанной длине.[StringLength]: Validates that a string property value doesn't exceed a specified length limit.
  • [Url]. проверяет, имеет ли свойство формат URL-адреса.[Url]: Validates that the property has a URL format.
  • [Remote]. проверяет входные данные на клиенте путем вызова метода действия на сервере.[Remote]: Validates input on the client by calling an action method on the server. Дополнительные сведения о поведении этого атрибута см. в разделе Атрибут [Remote].See [Remote] attribute for details about this attribute's behavior.

Полный перечень атрибутов проверки можно найти в пространстве имен System.ComponentModel.DataAnnotations.A complete list of validation attributes can be found in the System.ComponentModel.DataAnnotations namespace.

Сообщения об ошибкахError messages

Атрибуты проверки позволяют указать сообщение об ошибке, которое будет отображаться, если входные данные недопустимы.Validation attributes let you specify the error message to be displayed for invalid input. Пример:For example:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

На внутреннем уровне атрибуты вызывают String.Format с заполнителем для имени поля и иногда дополнительным заполнителями.Internally, the attributes call String.Format with a placeholder for the field name and sometimes additional placeholders. Пример:For example:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

При применении к свойству Name сообщение об ошибке, созданное в приведенном выше коде, имело бы вид Name length must be between 6 and 8 (Длина имени должна быть от 6 до 8).When applied to a Name property, the error message created by the preceding code would be "Name length must be between 6 and 8.".

Чтобы узнать, какие параметры передаются в String.Format для сообщения об ошибке определенного атрибута, см. раздел Исходный код DataAnnotations.To find out which parameters are passed to String.Format for a particular attribute's error message, see the DataAnnotations source code.

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

Система проверки в .NET Core 3.0 и более поздних версий рассматривает не допускающие значение NULL параметры или свойства привязки так, как если бы они имели атрибут [Required].The validation system in .NET Core 3.0 and later treats non-nullable parameters or bound properties as if they had a [Required] attribute. Типы значений, например decimal и int, не поддерживают значение NULL.Value types such as decimal and int are non-nullable. Это поведение можно отключить с помощью параметра SuppressImplicitRequiredAttributeForNonNullableReferenceTypes в Startup.ConfigureServices:This behavior can be disabled by configuring SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Startup.ConfigureServices:

services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Проверка [Required] на сервере[Required] validation on the server

На сервере обязательное значение считается отсутствующим, если свойство имеет значение NULL.On the server, a required value is considered missing if the property is null. Поле, не допускающее значения NULL, всегда является допустимым, и сообщение об ошибке атрибута [Required] никогда не выводится.A non-nullable field is always valid, and the [Required] attribute's error message is never displayed.

Тем не менее привязка модели для ненулевого свойства может завершиться ошибкой, приводящей к сообщению об ошибке, например The value '' is invalid.However, model binding for a non-nullable property may fail, resulting in an error message such as The value '' is invalid. Чтобы задать настраиваемое сообщение об ошибке во время проверки не допускающих значения NULL типов на стороне сервера, у вас есть следующие варианты.To specify a custom error message for server-side validation of non-nullable types, you have the following options:

  • Сделать поле допускающим значение NULL (например, decimal? вместо decimal).Make the field nullable (for example, decimal? instead of decimal). Типы значений Nullable<T> обрабатываются как стандартные типы, допускающие значение NULL.Nullable<T> value types are treated like standard nullable types.

  • Указать сообщение об ошибке по умолчанию для использования в привязке модели, как показано в следующем примере.Specify the default error message to be used by model binding, as shown in the following example:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

    Дополнительные сведения об ошибках привязки модели, у которых можно задать сообщение по умолчанию, см. в разделе DefaultModelBindingMessageProvider.For more information about model binding errors that you can set default messages for, see DefaultModelBindingMessageProvider.

Проверка [Required] на клиенте[Required] validation on the client

Типы и строки, не допускающие значение NULL, обрабатываются на клиенте не так, как на сервере.Non-nullable types and strings are handled differently on the client compared to the server. На клиенте:On the client:

  • Значение считается присутствующим только в том случае, если для него вводятся данные.A value is considered present only if input is entered for it. Таким образом, проверка на стороне клиента обрабатывает типы, не допускающие значение NULL, так же, как обнуляемые типы.Therefore, client-side validation handles non-nullable types the same as nullable types.
  • Пробелы в поле строки считаются допустимыми входными данными при проверке методом jQuery required.Whitespace in a string field is considered valid input by the jQuery Validation required method. Проверка на стороне сервера считает, что обязательное строковое поле недопустимо, если введены только пробелы.Server-side validation considers a required string field invalid if only whitespace is entered.

Как отмечалось ранее, не допускающие значение NULL типы рассматриваются как имеющие атрибут [Required].As noted earlier, non-nullable types are treated as though they had a [Required] attribute. Это означает, что вы получаете проверку на стороне клиента, даже если не применять атрибут [Required].That means you get client-side validation even if you don't apply the [Required] attribute. Но если вы не используете атрибут, вы получаете сообщение об ошибке по умолчанию.But if you don't use the attribute, you get a default error message. Чтобы задать настраиваемое сообщение об ошибке, используйте атрибут.To specify a custom error message, use the attribute.

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

Атрибут [Remote] реализует проверку на стороне клиента, в ходе которой требуется вызвать метод на сервере, чтобы определить, является ли допустимым поле ввода.The [Remote] attribute implements client-side validation that requires calling a method on the server to determine whether field input is valid. Например, приложению может потребоваться проверить, занято ли имя пользователя.For example, the app may need to verify whether a user name is already in use.

Для реализации удаленной проверки сделайте следующее.To implement remote validation:

  1. Создайте для вызова из JavaScript метод действия.Create an action method for JavaScript to call. Метод проверки jQuery remote ожидает ответ JSON.The jQuery Validate remote method expects a JSON response:

    • true означает, что входные данные допустимы.true means the input data is valid.
    • false, undefined или null означают, что входные данные недопустимы.false, undefined, or null means the input is invalid. Вывод стандартного сообщения об ошибкеDisplay the default error message.
    • Все прочие значения означают, что входные данные недопустимы.Any other string means the input is invalid. Вывод строки как настраиваемого сообщения об ошибке.Display the string as a custom error message.

    Ниже приведен пример метода действия, который возвращает настраиваемое сообщение об ошибке.Here's an example of an action method that returns a custom error message:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. В классе модели пометьте свойство атрибутом [Remote], указывающим метод действия проверки, как показано в следующем примере.In the model class, annotate the property with a [Remote] attribute that points to the validation action method, as shown in the following example:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; }
    

    Атрибут [Remote] находится в пространстве имен Microsoft.AspNetCore.Mvc.The [Remote] attribute is in the Microsoft.AspNetCore.Mvc namespace.

Дополнительные поляAdditional fields

Свойство AdditionalFields атрибута [Remote] позволяет проверять сочетания полей с данными на сервере.The AdditionalFields property of the [Remote] attribute lets you validate combinations of fields against data on the server. Например, если бы в модели User были свойства FirstName и LastName, могла бы возникнуть необходимость проверить, нет ли уже пользователя с такой парой имен.For example, if the User model had FirstName and LastName properties, you might want to verify that no existing users already have that pair of names. В следующем примере показано использование AdditionalFields.The following example shows how to use AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; }

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; }

AdditionalFields можно явно присвоить строкам "FirstName" и "LastName", но использование оператора nameof упрощает дальнейший рефакторинг.AdditionalFields could be set explicitly to the strings "FirstName" and "LastName", but using the nameof operator simplifies later refactoring. Методу действия для этой проверки необходимо принимать аргументы firstName и lastNameThe action method for this validation must accept both firstName and lastName arguments:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Когда пользователь вводит имя или фамилию, JavaScript выполняет удаленный вызов, чтобы увидеть, будет ли эта пара принята.When the user enters a first or last name, JavaScript makes a remote call to see if that pair of names has been taken.

Чтобы проверить несколько дополнительных полей, их следует указывать в виде списка с разделителями-запятыми.To validate two or more additional fields, provide them as a comma-delimited list. Например, чтобы добавить в модель свойство MiddleName, задайте атрибут [Remote], как показано в следующем примере.For example, to add a MiddleName property to the model, set the [Remote] attribute as shown in the following example:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, как и все аргументы атрибутов, должен представлять собой константное выражение.AdditionalFields, like all attribute arguments, must be a constant expression. Поэтому не следует использовать интерполированную строку или вызов Joinдля инициализации AdditionalFields.Therefore, don't use an interpolated string or call Join to initialize AdditionalFields.

Альтернативы для встроенных атрибутовAlternatives to built-in attributes

Если вам нужна проверка, которую не предоставляют встроенные атрибуты, вы можете следующее.If you need validation not provided by built-in attributes, you can:

Настраиваемые атрибутыCustom attributes

Для сценариев, где не годятся встроенные атрибуты проверки, можно создать настраиваемые атрибуты.For scenarios that the built-in validation attributes don't handle, you can create custom validation attributes. Создайте класс, наследуемый от ValidationAttribute, и переопределите метод IsValid.Create a class that inherits from ValidationAttribute, and override the IsValid method.

Метод IsValid принимает объект с именем value, который является входными данными для проверки.The IsValid method accepts an object named value, which is the input to be validated. Перегрузка также принимает объект ValidationContext, который предоставляет дополнительные сведения, такие как экземпляр модели, созданный с помощью привязки модели.An overload also accepts a ValidationContext object, which provides additional information, such as the model instance created by model binding.

В следующем примере проверяется, что дата выпуска фильмов в классическом жанре задана не позднее указанного года.The following example validates that the release date for a movie in the Classic genre isn't later than a specified year. Атрибут [ClassicMovie]:The [ClassicMovie] attribute:

  • выполняется только на сервере.Is only run on the server.
  • Для классических фильмов проверяет дату выпуска:For Classic movies, validates the release date:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
    {
        Year = year;
    }

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult IsValid(object value,
        ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

Приведенная выше переменная movie представляет объект Movie, который содержит данные из переданной формы.The movie variable in the preceding example represents a Movie object that contains the data from the form submission. Если проверка завершается неудачно, возвращается ValidationResult с сообщением об ошибке.When validation fails, a ValidationResult with an error message is returned.

IValidatableObjectIValidatableObject

Предыдущий пример работает только с типами Movie.The preceding example works only with Movie types. Другой вариант для проверки на уровне класса — реализация IValidatableObject в классе модели, как показано в следующем примере.Another option for class-level validation is to implement IValidatableObject in the model class, as shown in the following example:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Проверка узлов верхнего уровняTop-level node validation

Узлы верхнего уровня содержат:Top-level nodes include:

  • параметры действия;Action parameters
  • свойства контроллера;Controller properties
  • параметры обработчика страниц;Page handler parameters
  • свойства страничной модели.Page model properties

Проверка привязанных к модели узлов верхнего уровня осуществляется наряду с проверкой свойств модели.Model-bound top-level nodes are validated in addition to validating model properties. В следующем примере, взятом из примера приложения, метод VerifyPhone использует класс RegularExpressionAttribute для проверки параметра действия phone.In the following example from the sample app, the VerifyPhone method uses the RegularExpressionAttribute to validate the phone action parameter:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Узлы верхнего уровня могут применять класс BindRequiredAttribute с атрибутами проверки.Top-level nodes can use BindRequiredAttribute with validation attributes. В следующем примере, взятом из примера приложения, метод CheckAge указывает, что при отправке формы параметр age должен быть привязан из строки запроса.In the following example from the sample app, the CheckAge method specifies that the age parameter must be bound from the query string when the form is submitted:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

На странице "Check Age" (Проверка возраста) (CheckAge.cshtml) находятся две формы.In the Check Age page (CheckAge.cshtml), there are two forms. Первая форма отправляет значение Age, равное 99, в виде параметра строки запроса: https://localhost:5001/Users/CheckAge?Age=99.The first form submits an Age value of 99 as a query string parameter: https://localhost:5001/Users/CheckAge?Age=99.

Если из строки запроса отправлен параметр age в правильном формате, форма проходит проверку.When a properly formatted age parameter from the query string is submitted, the form validates.

Вторая форма на странице "Check Age" (Проверка возраста) отправляет значение Age в теле запроса, и проверка завершается сбоем.The second form on the Check Age page submits the Age value in the body of the request, and validation fails. Ошибка привязки связана с тем, что параметр age должен поступать из строки запроса.Binding fails because the age parameter must come from a query string.

Максимальное количество ошибокMaximum errors

При достижении максимального количества ошибок (по умолчанию 200) проверка прекращается.Validation stops when the maximum number of errors is reached (200 by default). Это число можно изменить с помощью следующего кода в Startup.ConfigureServices:You can configure this number with the following code in Startup.ConfigureServices:

services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

Максимальная рекурсияMaximum recursion

ValidationVisitor проходит через граф объектов в проверяемой модели.ValidationVisitor traverses the object graph of the model being validated. У глубоких моделей, содержащих бесконечную рекурсию, в ходе проверки может произойти переполнение стека.For models that are deep or are infinitely recursive, validation may result in stack overflow. MvcOptions.MaxValidationDepth предоставляет способ остановить проверку до превышения настроенной глубины рекурсии обхода.MvcOptions.MaxValidationDepth provides a way to stop validation early if the visitor recursion exceeds a configured depth. Значение MvcOptions.MaxValidationDepth по умолчанию —32.The default value of MvcOptions.MaxValidationDepth is 32.

Автоматическое сокращениеAutomatic short-circuit

Проверка автоматически сокращается (пропускается), если граф модели не требует проверки.Validation is automatically short-circuited (skipped) if the model graph doesn't require validation. К числу объектов, которые среда выполнения пропускает при проверке, относятся коллекции примитивов (такие как byte[], string[], Dictionary<string, string>) и сложные графы объектов, которые не имеют проверяющих элементов управления.Objects that the runtime skips validation for include collections of primitives (such as byte[], string[], Dictionary<string, string>) and complex object graphs that don't have any validators.

Отключение проверкиDisable validation

Чтобы отключить проверку, сделайте следующее.To disable validation:

  1. Создайте реализацию интерфейса IObjectModelValidator, которая не помечает поля как недопустимые.Create an implementation of IObjectModelValidator that doesn't mark any fields as invalid.

    public class NullObjectModelValidator : IObjectModelValidator
    {
        public void Validate(ActionContext actionContext,
            ValidationStateDictionary validationState, string prefix, object model)
        {
    
        }
    }
    
  2. Добавьте следующий код в Startup.ConfigureServices для замены реализации IObjectModelValidator по умолчанию в контейнере внедрения зависимостей.Add the following code to Startup.ConfigureServices to replace the default IObjectModelValidator implementation in the dependency injection container.

    services.AddSingleton<IObjectModelValidator, NullObjectModelValidator>();
    

По-прежнему могут отображаться ошибки состояния модели, которые идут из привязки модели.You might still see model state errors that originate from model binding.

Проверка на стороне клиентаClient-side validation

Проверка на стороне клиента не позволяет отправлять форму, пока ее данные не будут допустимыми.Client-side validation prevents submission until the form is valid. При нажатии кнопки "Отправить" выполняется код JavaScript, который либо отправляет форму, либо выводит сообщения об ошибках.The Submit button runs JavaScript that either submits the form or displays error messages.

Проверка на стороне клиента позволяет избежать ненужного кругового захода на сервер при наличии ошибки ввода в форме.Client-side validation avoids an unnecessary round trip to the server when there are input errors on a form. Ссылки на следующий скрипт в _Layout.cshtml и _ValidationScriptsPartial.cshtml поддерживают проверку на стороне клиента.The following script references in _Layout.cshtml and _ValidationScriptsPartial.cshtml support client-side validation:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>

Скрипт ненавязчивой проверки jQuery — это настраиваемая интерфейсная библиотека Майкрософт, которая основана на популярном подключаемом модуле jQuery Validate.The jQuery Unobtrusive Validation script is a custom Microsoft front-end library that builds on the popular jQuery Validate plugin. Без скрипта ненавязчивой проверки jQuery одну и ту же логику проверки приходилось бы реализовывать в двух местах: в атрибутах проверки для свойств модели на стороне сервера, а затем еще раз в скриптах на стороне клиента.Without jQuery Unobtrusive Validation, you would have to code the same validation logic in two places: once in the server-side validation attributes on model properties, and then again in client-side scripts. Вместо этого вспомогательные функции тегов и вспомогательные функции HTML могут использовать атрибуты проверки и метаданные типов из свойств модели для обработки атрибутов data- HTML 5 в элементах форм, требующих проверки.Instead, Tag Helpers and HTML helpers use the validation attributes and type metadata from model properties to render HTML 5 data- attributes for the form elements that need validation. Скрипт ненавязчивой проверки jQuery анализирует эти атрибуты data- и передает логику в подключаемый модуль jQuery Validate, по сути копируя логику проверки на стороне сервера в клиент.jQuery Unobtrusive Validation parses the data- attributes and passes the logic to jQuery Validate, effectively "copying" the server-side validation logic to the client. Ошибки проверки могут выводиться в клиенте с помощью вспомогательных функций тегов, как показано ниже.You can display validation errors on the client using tag helpers as shown here:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

Приведенные выше вспомогательные функции тегов отрисовывают следующий код HTML:The preceding tag helpers render the following HTML:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Обратите внимание на то, что атрибуты data- в выходных данных HTML соответствуют атрибутам проверки для свойства Movie.ReleaseDate.Notice that the data- attributes in the HTML output correspond to the validation attributes for the Movie.ReleaseDate property. Атрибут data-val-required содержит сообщение об ошибке, которое выводится, если пользователь не заполнил поле даты выхода.The data-val-required attribute contains an error message to display if the user doesn't fill in the release date field. Скрипт ненавязчивой проверки jQuery передает это значение в метод required() подключаемого модуля jQuery Validate, который затем выводит это сообщение в соответствующем элементе <span> .jQuery Unobtrusive Validation passes this value to the jQuery Validate required() method, which then displays that message in the accompanying <span> element.

Проверка типа данных основана на типе свойства в .NET, если его не переопределяет атрибут [DataType].Data type validation is based on the .NET type of a property, unless that is overridden by a [DataType] attribute. Браузеры имеют свои сообщения об по умолчанию, но пакет ненавязчивой проверки jQuery может переопределять эти сообщения.Browsers have their own default error messages, but the jQuery Validation Unobtrusive Validation package can override those messages. Атрибуты и подклассы [DataType], такие как [EmailAddress], позволяют указать сообщение об ошибке.[DataType] attributes and subclasses such as [EmailAddress] let you specify the error message.

Ненавязчивая проверкаUnobtrusive validation

См. сведения о ненавязчивой проверке в этой проблеме GitHub.For information on unobtrusive validation, see this GitHub issue.

Добавление проверки к динамическим формамAdd Validation to Dynamic Forms

Скрипт ненавязчивой проверки jQuery передает логику и параметры проверки в подключаемый модуль jQuery Validate при первой загрузке страницы.jQuery Unobtrusive Validation passes validation logic and parameters to jQuery Validate when the page first loads. Поэтому динамически создаваемые формы не подвергаются проверке автоматически.Therefore, validation doesn't work automatically on dynamically generated forms. Чтобы включить проверку, необходимо указать, что скрипт ненавязчивой проверки jQuery должен анализировать динамическую форму сразу после ее создания.To enable validation, tell jQuery Unobtrusive Validation to parse the dynamic form immediately after you create it. Например, приведенный ниже код показывает, как можно настроить проверку на стороне клиента для формы, добавленной посредством AJAX.For example, the following code sets up client-side validation on a form added via AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Метод $.validator.unobtrusive.parse() принимает селектор jQuery в качестве единственного аргумента.The $.validator.unobtrusive.parse() method accepts a jQuery selector for its one argument. Этот метод предписывает скрипту ненавязчивой проверки jQuery анализировать атрибуты data- форм в этом селекторе.This method tells jQuery Unobtrusive Validation to parse the data- attributes of forms within that selector. Значения этих атрибутов затем передаются в подключаемый модуль jQuery Validate.The values of those attributes are then passed to the jQuery Validate plugin.

Добавление проверки к динамическим элементам управленияAdd Validation to Dynamic Controls

Метод $.validator.unobtrusive.parse() обрабатывает всю форму, а не отдельные динамически создаваемые элементы управления, такие как <input> и <select/>.The $.validator.unobtrusive.parse() method works on an entire form, not on individual dynamically generated controls, such as <input> and <select/>. Для повторной обработки формы удалите данные проверки, которые были добавлены при анализе формы ранее, как показано в следующем примере:To reparse the form, remove the validation data that was added when the form was parsed earlier, as shown in the following example:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validate
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Настраиваемая проверка на стороне клиентаCustom client-side validation

Настраиваемая проверка на стороне клиента выполняется путем создания атрибутов HTML data-, которые работают с настраиваемым адаптером проверки jQuery.Custom client-side validation is done by generating data- HTML attributes that work with a custom jQuery Validate adapter. В следующем примере кода адаптера используются атрибуты [ClassicMovie] и [ClassicMovieWithClientValidator], которые были введены ранее в этой статье.The following sample adapter code was written for the [ClassicMovie] and [ClassicMovieWithClientValidator] attributes that were introduced earlier in this article:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Сведения о способах создания адаптеров см. в документации по проверкам jQuery.For information about how to write adapters, see the jQuery Validate documentation.

Использование адаптера для заданного поля инициируется атрибутами data-, которые:The use of an adapter for a given field is triggered by data- attributes that:

  • помечают поле как проходящее проверку (data-val="true");Flag the field as being subject to validation (data-val="true").
  • указывают имя правила проверки и текст сообщения об ошибке (например, data-val-rulename="Error message.");Identify a validation rule name and error message text (for example, data-val-rulename="Error message.").
  • указывают любые дополнительные параметры, в которых нуждается проверяющий элемент управления (например, data-val-rulename-param1="value").Provide any additional parameters the validator needs (for example, data-val-rulename-param1="value").

В следующем примере показаны атрибуты data- для нашего атрибута ClassicMovie из примера приложения.The following example shows the data- attributes for the sample app's ClassicMovie attribute:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Как отмечалось ранее, вспомогательные функции тегов и вспомогательные методы HTML используют сведения из атрибутов проверки для подготовки к отрисовке атрибутов data-.As noted earlier, Tag Helpers and HTML helpers use information from validation attributes to render data- attributes. Существует два варианта для написания кода, который приводит к созданию настраиваемых атрибутов HTML data-.There are two options for writing code that results in the creation of custom data- HTML attributes:

  • Создайте класс, производный от AttributeAdapterBase<TAttribute>, и класс, реализующий IValidationAttributeAdapterProvider; зарегистрируйте атрибут и его адаптер в функции внедрения зависимостей.Create a class that derives from AttributeAdapterBase<TAttribute> and a class that implements IValidationAttributeAdapterProvider, and register your attribute and its adapter in DI. Этот метод следует за участником с единственной обязанностью в том, что код проверки, связанный с сервером и клиентом, выделен в отдельные классы.This method follows the single responsibility principal in that server-related and client-related validation code is in separate classes. Адаптер также имеет следующее преимущество: так как он зарегистрирован в функции внедрения зависимостей, для него при необходимости доступны другие службы в ней.The adapter also has the advantage that since it is registered in DI, other services in DI are available to it if needed.
  • Реализуйте IClientModelValidator в вашем классе ValidationAttribute.Implement IClientModelValidator in your ValidationAttribute class. Этот метод стоит использовать, если атрибут не выполняет проверку на стороне сервера и не нуждается в службах из функции внедрения зависимостей.This method might be appropriate if the attribute doesn't do any server-side validation and doesn't need any services from DI.

AttributeAdapter для проверки на стороне клиентаAttributeAdapter for client-side validation

Этот метод отрисовки атрибутов data- в формате HTML используется атрибутом ClassicMovie в примере приложения.This method of rendering data- attributes in HTML is used by the ClassicMovie attribute in the sample app. Чтобы добавить клиентскую проверку с помощью этого метода, сделайте следующее.To add client validation by using this method:

  1. Создайте класс адаптера атрибута для настраиваемого атрибута проверки.Create an attribute adapter class for the custom validation attribute. Наследуйте класс от AttributeAdapterBase<T>.Derive the class from AttributeAdapterBase<T>. Создайте метод AddValidation, который добавляет атрибуты data- для выводимых данных, как показано в следующем примере.Create an AddValidation method that adds data- attributes to the rendered output, as shown in this example:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute,
            IStringLocalizer stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext) =>
            Attribute.GetErrorMessage();
    }
    
  2. Создайте класс поставщика адаптера, который реализует IValidationAttributeAdapterProvider.Create an adapter provider class that implements IValidationAttributeAdapterProvider. В методе GetAttributeAdapter передайте настраиваемый атрибут в конструктор адаптера, как показано в следующем примере.In the GetAttributeAdapter method pass in the custom attribute to the adapter's constructor, as shown in this example:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
            IStringLocalizer stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Зарегистрируйте поставщик адаптера для внедрения зависимостей в Startup.ConfigureServices.Register the adapter provider for DI in Startup.ConfigureServices:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator для проверки на стороне клиентаIClientModelValidator for client-side validation

Этот метод отрисовки атрибутов data- в формате HTML используется атрибутом ClassicMovieWithClientValidator в примере приложения.This method of rendering data- attributes in HTML is used by the ClassicMovieWithClientValidator attribute in the sample app. Чтобы добавить клиентскую проверку с помощью этого метода, сделайте следующее.To add client validation by using this method:

  • В настраиваемом атрибуте проверки реализуйте интерфейс IClientModelValidator и создайте метод AddValidation.In the custom validation attribute, implement the IClientModelValidator interface and create an AddValidation method. В метод AddValidation добавьте атрибуты data- для проверки, как показано в следующем примере.In the AddValidation method, add data- attributes for validation, as shown in the following example:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
        {
            Year = year;
        }
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult IsValid(object value,
            ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Отключение проверки на стороне клиентаDisable client-side validation

Следующий код отключает клиентскую проверку в Razor Pages:The following code disables client validation in Razor Pages:

services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Другие параметры отключения проверки на стороне клиента:Other options to disable client-side validation:

  • Закомментируйте ссылку на _ValidationScriptsPartial во всех файлах с расширением CSHTML.Comment out the reference to _ValidationScriptsPartial in all the .cshtml files.
  • Удалите содержимое файла Pages\Shared_ValidationScriptsPartial.cshtml.Remove the contents of the Pages\Shared_ValidationScriptsPartial.cshtml file.

Предыдущий подход не помешает проверке на стороне клиента библиотеки классов Razor для ASP.NET Core Identity.The preceding approach won't prevent client side validation of ASP.NET Core Identity Razor Class Library. Для получения дополнительной информации см. Удостоверение шаблона в ASP.NET Core проектах.For more information, see Удостоверение шаблона в ASP.NET Core проектах.

Дополнительные ресурсыAdditional resources

В этой статье объясняется, как осуществлять проверки вводимых пользователем данных в приложении ASP.NET Core MVC или Razor Pages.This article explains how to validate user input in an ASP.NET Core MVC or Razor Pages app.

Просмотреть или скачать пример кода (описание скачивания).View or download sample code (how to download).

Состояние моделиModel state

Состояние модели представляет ошибки, создаваемые двумя подсистемами: привязкой модели и проверкой модели.Model state represents errors that come from two subsystems: model binding and model validation. Ошибки, поступающие из привязки модели, чаще всего представляют собой ошибки преобразования данных (например, x вводится в поле, которое ожидает целое число).Errors that originate from model binding are generally data conversion errors (for example, an "x" is entered in a field that expects an integer). Проверка модели проводится после привязки модели и сообщает об ошибках, при которых данные не соответствуют бизнес-правилам (например, 0 вводится в поле, которое ожидает оценку от 1 до 5).Model validation occurs after model binding and reports errors where the data doesn't conform to business rules (for example, a 0 is entered in a field that expects a rating between 1 and 5).

Привязка модели и проверка модели происходят перед выполнением действия контроллера или метода обработчика Razor Pages.Both model binding and validation occur before the execution of a controller action or a Razor Pages handler method. Веб-приложение отвечает за проверку ModelState.IsValid и реагирует соответствующим образом.For web apps, it's the app's responsibility to inspect ModelState.IsValid and react appropriately. Веб-приложения обычно повторно отображают страницы с сообщением об ошибке.Web apps typically redisplay the page with an error message:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Контроллерам веб-API не нужно проверять ModelState.IsValid при наличии атрибута [ApiController].Web API controllers don't have to check ModelState.IsValid if they have the [ApiController] attribute. В этом случае автоматически возвращается ответ HTTP 400, содержащий сведения об ошибке, если состояние модели недопустимо.In that case, an automatic HTTP 400 response containing error details is returned when model state is invalid. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400.For more information, see Automatic HTTP 400 responses.

Перезапуск проверкиRerun validation

Проверка выполняется автоматически, однако может потребоваться повторить ее вручную.Validation is automatic, but you might want to repeat it manually. Например, можно вычислить значение свойства и повторно выполнить проверку после установки свойства в вычисляемое значение.For example, you might compute a value for a property and want to rerun validation after setting the property to the computed value. Для повторной проверки вызовите метод TryValidateModel, как показано ниже.To rerun validation, call the TryValidateModel method, as shown here:

var movie = new Movie
{
    Title = title,
    Genre = genre,
    ReleaseDate = modifiedReleaseDate,
    Description = description,
    Price = price,
    Preorder = preorder,
};

TryValidateModel(movie);

if (ModelState.IsValid)
{
    _context.AddMovie(movie);
    _context.SaveChanges();

    return RedirectToAction(actionName: nameof(Index));
}

return View(movie);

Атрибуты проверкиValidation attributes

Атрибуты проверки позволяют задать правила проверки для свойств модели.Validation attributes let you specify validation rules for model properties. В следующем примере из примера приложения показан класс модели, который помечается с помощью атрибутов проверки.The following example from the sample app shows a model class that is annotated with validation attributes. [ClassicMovie] является настраиваемым атрибутом проверки; остальные являются встроенными.The [ClassicMovie] attribute is a custom validation attribute and the others are built-in. Не показан [ClassicMovie2], который демонстрирует альтернативный способ реализации настраиваемого атрибута.Not shown is [ClassicMovie2], which shows an alternative way to implement a custom attribute.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    [Required]
    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Встроенные атрибутыBuilt-in attributes

К встроенным атрибутам проверки относятся:Built-in validation attributes include:

  • [CreditCard]. проверяет, имеет ли свойство формат кредитной карты.[CreditCard]: Validates that the property has a credit card format.
  • [Compare]. проверяет, совпадают ли два свойства модели.[Compare]: Validates that two properties in a model match. Например, файл Register.cshtml.cs использует [Compare] для проверки совпадений двух введенных паролей.For example, the Register.cshtml.cs file uses [Compare] to validate the two entered passwords match. Удостоверение шаблона для просмотра кода регистрации.Scaffold Identity to see the Register code.
  • [EmailAddress]. проверяет, имеет ли свойство формат адреса электронной почты.[EmailAddress]: Validates that the property has an email format.
  • [Phone]. проверяет, имеет ли свойство формат номера телефона.[Phone]: Validates that the property has a telephone number format.
  • [Range]. проверяет, находится ли значение свойства в указанном диапазоне.[Range]: Validates that the property value falls within a specified range.
  • [RegularExpression]. проверяет, соответствует ли значение свойства указанному регулярному выражению.[RegularExpression]: Validates that the property value matches a specified regular expression.
  • [Required]. проверяет, что поле не равно NULL.[Required]: Validates that the field is not null. Дополнительные сведения о поведении этого атрибута см. в разделе Атрибут [Required].See [Required] attribute for details about this attribute's behavior.
  • [StringLength]. проверяет, что значение свойства строки не превышает ограничение по указанной длине.[StringLength]: Validates that a string property value doesn't exceed a specified length limit.
  • [Url]. проверяет, имеет ли свойство формат URL-адреса.[Url]: Validates that the property has a URL format.
  • [Remote]. проверяет входные данные на клиенте путем вызова метода действия на сервере.[Remote]: Validates input on the client by calling an action method on the server. Дополнительные сведения о поведении этого атрибута см. в разделе Атрибут [Remote].See [Remote] attribute for details about this attribute's behavior.

При использовании атрибута [RegularExpression] с проверкой на стороне клиента регулярное выражение выполняется в JavaScript в клиенте.When using the [RegularExpression] attribute with client-side validation, the regex is executed in JavaScript on the client. Это означает, что будет использоваться поведение сопоставления ECMAScript.This means ECMAScript matching behavior will be used. Дополнительные сведения см. в этой статье об ошибке на GitHub.For more information, see this GitHub issue.

Полный перечень атрибутов проверки можно найти в пространстве имен System.ComponentModel.DataAnnotations.A complete list of validation attributes can be found in the System.ComponentModel.DataAnnotations namespace.

Сообщения об ошибкахError messages

Атрибуты проверки позволяют указать сообщение об ошибке, которое будет отображаться, если входные данные недопустимы.Validation attributes let you specify the error message to be displayed for invalid input. Пример:For example:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

На внутреннем уровне атрибуты вызывают String.Format с заполнителем для имени поля и иногда дополнительным заполнителями.Internally, the attributes call String.Format with a placeholder for the field name and sometimes additional placeholders. Пример:For example:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

При применении к свойству Name сообщение об ошибке, созданное в приведенном выше коде, имело бы вид Name length must be between 6 and 8 (Длина имени должна быть от 6 до 8).When applied to a Name property, the error message created by the preceding code would be "Name length must be between 6 and 8.".

Чтобы узнать, какие параметры передаются в String.Format для сообщения об ошибке определенного атрибута, см. раздел Исходный код DataAnnotations.To find out which parameters are passed to String.Format for a particular attribute's error message, see the DataAnnotations source code.

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

По умолчанию система проверки рассматривает не допускающие значение NULL параметры или свойства так, как если бы они имели атрибут [Required].By default, the validation system treats non-nullable parameters or properties as if they had a [Required] attribute. Типы значений, например decimal и int, не поддерживают значение NULL.Value types such as decimal and int are non-nullable.

Проверка [Required] на сервере[Required] validation on the server

На сервере обязательное значение считается отсутствующим, если свойство имеет значение NULL.On the server, a required value is considered missing if the property is null. Поле, не допускающее значения NULL, всегда является допустимым, и сообщение об ошибке атрибута [Required] никогда не выводится.A non-nullable field is always valid, and the [Required] attribute's error message is never displayed.

Тем не менее привязка модели для ненулевого свойства может завершиться ошибкой, приводящей к сообщению об ошибке, например The value '' is invalid.However, model binding for a non-nullable property may fail, resulting in an error message such as The value '' is invalid. Чтобы задать настраиваемое сообщение об ошибке во время проверки не допускающих значения NULL типов на стороне сервера, у вас есть следующие варианты.To specify a custom error message for server-side validation of non-nullable types, you have the following options:

  • Сделать поле допускающим значение NULL (например, decimal? вместо decimal).Make the field nullable (for example, decimal? instead of decimal). Типы значений Nullable<T> обрабатываются как стандартные типы, допускающие значение NULL.Nullable<T> value types are treated like standard nullable types.

  • Указать сообщение об ошибке по умолчанию для использования в привязке модели, как показано в следующем примере.Specify the default error message to be used by model binding, as shown in the following example:

    services.AddMvc(options => 
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                (_) => "The field is required.");
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddSingleton
        <IValidationAttributeAdapterProvider, 
         CustomValidationAttributeAdapterProvider>();
    

    Дополнительные сведения об ошибках привязки модели, у которых можно задать сообщение по умолчанию, см. в разделе DefaultModelBindingMessageProvider.For more information about model binding errors that you can set default messages for, see DefaultModelBindingMessageProvider.

Проверка [Required] на клиенте[Required] validation on the client

Типы и строки, не допускающие значение NULL, обрабатываются на клиенте не так, как на сервере.Non-nullable types and strings are handled differently on the client compared to the server. На клиенте:On the client:

  • Значение считается присутствующим только в том случае, если для него вводятся данные.A value is considered present only if input is entered for it. Таким образом, проверка на стороне клиента обрабатывает типы, не допускающие значение NULL, так же, как обнуляемые типы.Therefore, client-side validation handles non-nullable types the same as nullable types.
  • Пробелы в поле строки считаются допустимыми входными данными при проверке методом jQuery required.Whitespace in a string field is considered valid input by the jQuery Validation required method. Проверка на стороне сервера считает, что обязательное строковое поле недопустимо, если введены только пробелы.Server-side validation considers a required string field invalid if only whitespace is entered.

Как отмечалось ранее, не допускающие значение NULL типы рассматриваются как имеющие атрибут [Required].As noted earlier, non-nullable types are treated as though they had a [Required] attribute. Это означает, что вы получаете проверку на стороне клиента, даже если не применять атрибут [Required].That means you get client-side validation even if you don't apply the [Required] attribute. Но если вы не используете атрибут, вы получаете сообщение об ошибке по умолчанию.But if you don't use the attribute, you get a default error message. Чтобы задать настраиваемое сообщение об ошибке, используйте атрибут.To specify a custom error message, use the attribute.

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

Атрибут [Remote] реализует проверку на стороне клиента, в ходе которой требуется вызвать метод на сервере, чтобы определить, является ли допустимым поле ввода.The [Remote] attribute implements client-side validation that requires calling a method on the server to determine whether field input is valid. Например, приложению может потребоваться проверить, занято ли имя пользователя.For example, the app may need to verify whether a user name is already in use.

Для реализации удаленной проверки сделайте следующее.To implement remote validation:

  1. Создайте для вызова из JavaScript метод действия.Create an action method for JavaScript to call. Метод проверки jQuery remote ожидает ответ JSON.The jQuery Validate remote method expects a JSON response:

    • "true" означает, что входные данные допустимы."true" means the input data is valid.
    • "false", undefined или null означают, что входные данные недопустимы."false", undefined, or null means the input is invalid. Вывод стандартного сообщения об ошибкеDisplay the default error message.
    • Все прочие значения означают, что входные данные недопустимы.Any other string means the input is invalid. Вывод строки как настраиваемого сообщения об ошибке.Display the string as a custom error message.

    Ниже приведен пример метода действия, который возвращает настраиваемое сообщение об ошибке.Here's an example of an action method that returns a custom error message:

    [AcceptVerbs("Get", "Post")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userRepository.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. В классе модели пометьте свойство атрибутом [Remote], указывающим метод действия проверки, как показано в следующем примере.In the model class, annotate the property with a [Remote] attribute that points to the validation action method, as shown in the following example:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; }
    

    Атрибут [Remote] находится в пространстве имен Microsoft.AspNetCore.Mvc.The [Remote] attribute is in the Microsoft.AspNetCore.Mvc namespace. Установите пакет NuGet Microsoft.AspNetCore.Mvc.ViewFeatures, если вы не используете метапакет Microsoft.AspNetCore.App или Microsoft.AspNetCore.All.Install the Microsoft.AspNetCore.Mvc.ViewFeatures NuGet package if you're not using the Microsoft.AspNetCore.App or Microsoft.AspNetCore.All metapackage.

Дополнительные поляAdditional fields

Свойство AdditionalFields атрибута [Remote] позволяет проверять сочетания полей с данными на сервере.The AdditionalFields property of the [Remote] attribute lets you validate combinations of fields against data on the server. Например, если бы в модели User были свойства FirstName и LastName, могла бы возникнуть необходимость проверить, нет ли уже пользователя с такой парой имен.For example, if the User model had FirstName and LastName properties, you might want to verify that no existing users already have that pair of names. В следующем примере показано использование AdditionalFields.The following example shows how to use AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
public string FirstName { get; set; }
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
public string LastName { get; set; }

AdditionalFields можно явно присвоить строкам "FirstName" и "LastName", но использование оператора nameof упрощает дальнейший рефакторинг.AdditionalFields could be set explicitly to the strings "FirstName" and "LastName", but using the nameof operator simplifies later refactoring. Методу действия для этой проверки необходимо принимать аргументы для имени и фамилии.The action method for this validation must accept both first name and last name arguments:

[AcceptVerbs("Get", "Post")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userRepository.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Когда пользователь вводит имя или фамилию, JavaScript выполняет удаленный вызов, чтобы увидеть, будет ли эта пара принята.When the user enters a first or last name, JavaScript makes a remote call to see if that pair of names has been taken.

Чтобы проверить несколько дополнительных полей, их следует указывать в виде списка с разделителями-запятыми.To validate two or more additional fields, provide them as a comma-delimited list. Например, чтобы добавить в модель свойство MiddleName, задайте атрибут [Remote], как показано в следующем примере.For example, to add a MiddleName property to the model, set the [Remote] attribute as shown in the following example:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, как и все аргументы атрибутов, должен представлять собой константное выражение.AdditionalFields, like all attribute arguments, must be a constant expression. Поэтому не следует использовать интерполированную строку или вызов Joinдля инициализации AdditionalFields.Therefore, don't use an interpolated string or call Join to initialize AdditionalFields.

Альтернативы для встроенных атрибутовAlternatives to built-in attributes

Если вам нужна проверка, которую не предоставляют встроенные атрибуты, вы можете следующее.If you need validation not provided by built-in attributes, you can:

Настраиваемые атрибутыCustom attributes

Для сценариев, где не годятся встроенные атрибуты проверки, можно создать настраиваемые атрибуты.For scenarios that the built-in validation attributes don't handle, you can create custom validation attributes. Создайте класс, наследуемый от ValidationAttribute, и переопределите метод IsValid.Create a class that inherits from ValidationAttribute, and override the IsValid method.

Метод IsValid принимает объект с именем value, который является входными данными для проверки.The IsValid method accepts an object named value, which is the input to be validated. Перегрузка также принимает объект ValidationContext, который предоставляет дополнительные сведения, такие как экземпляр модели, созданный с помощью привязки модели.An overload also accepts a ValidationContext object, which provides additional information, such as the model instance created by model binding.

В следующем примере проверяется, что дата выпуска фильмов в классическом жанре задана не позднее указанного года.The following example validates that the release date for a movie in the Classic genre isn't later than a specified year. Атрибут [ClassicMovie2] сначала проверяет жанр и продолжает проверку, только если он классический.The [ClassicMovie2] attribute checks the genre first and continues only if it's Classic. У фильмов, попавших в классику, система проверяет даты выпуска, чтобы убедиться в том, что она не является более поздней, чем ограничение, переданное в конструктор атрибута.For movies identified as classics, it checks the release date to make sure it's not later than the limit passed to the attribute constructor.)

public class ClassicMovieAttribute : ValidationAttribute
{
    private int _year;

    public ClassicMovieAttribute(int year)
    {
        _year = year;
    }

    protected override ValidationResult IsValid(
        object value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value).Year;

        if (movie.Genre == Genre.Classic && releaseYear > _year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }

    public int Year => _year;

    public string GetErrorMessage()
    {
        return $"Classic movies must have a release year no later than {_year}.";
    }
}

Приведенная выше переменная movie представляет объект Movie, который содержит данные из переданной формы.The movie variable in the preceding example represents a Movie object that contains the data from the form submission. Метод IsValid проверяет дату и жанр.The IsValid method checks the date and genre. После успешной проверки IsValid возвращает код ValidationResult.Success.Upon successful validation, IsValid returns a ValidationResult.Success code. Если проверка завершается неудачно, возвращается ValidationResult с сообщением об ошибке.When validation fails, a ValidationResult with an error message is returned.

IValidatableObjectIValidatableObject

Предыдущий пример работает только с типами Movie.The preceding example works only with Movie types. Другой вариант для проверки на уровне класса — реализация IValidatableObject в классе модели, как показано в следующем примере.Another option for class-level validation is to implement IValidatableObject in the model class, as shown in the following example:

public class MovieIValidatable : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

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

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    [Required]
    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year earlier than {_classicYear}.",
                new[] { "ReleaseDate" });
        }
    }
}

Проверка узлов верхнего уровняTop-level node validation

Узлы верхнего уровня содержат:Top-level nodes include:

  • параметры действия;Action parameters
  • свойства контроллера;Controller properties
  • параметры обработчика страниц;Page handler parameters
  • свойства страничной модели.Page model properties

Проверка привязанных к модели узлов верхнего уровня осуществляется наряду с проверкой свойств модели.Model-bound top-level nodes are validated in addition to validating model properties. В следующем примере, взятом из примера приложения, метод VerifyPhone использует класс RegularExpressionAttribute для проверки параметра действия phone.In the following example from the sample app, the VerifyPhone method uses the RegularExpressionAttribute to validate the phone action parameter:

[AcceptVerbs("Get", "Post")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Узлы верхнего уровня могут применять класс BindRequiredAttribute с атрибутами проверки.Top-level nodes can use BindRequiredAttribute with validation attributes. В следующем примере, взятом из примера приложения, метод CheckAge указывает, что при отправке формы параметр age должен быть привязан из строки запроса.In the following example from the sample app, the CheckAge method specifies that the age parameter must be bound from the query string when the form is submitted:

[HttpPost]
public IActionResult CheckAge(
    [BindRequired, FromQuery] int age)
{

На странице "Check Age" (Проверка возраста) (CheckAge.cshtml) находятся две формы.In the Check Age page (CheckAge.cshtml), there are two forms. Первая форма отправляет значение Age, равное 99, в виде строки запроса: https://localhost:5001/Users/CheckAge?Age=99.The first form submits an Age value of 99 as a query string: https://localhost:5001/Users/CheckAge?Age=99.

Если из строки запроса отправлен параметр age в правильном формате, форма проходит проверку.When a properly formatted age parameter from the query string is submitted, the form validates.

Вторая форма на странице "Check Age" (Проверка возраста) отправляет значение Age в теле запроса, и проверка завершается сбоем.The second form on the Check Age page submits the Age value in the body of the request, and validation fails. Ошибка привязки связана с тем, что параметр age должен поступать из строки запроса.Binding fails because the age parameter must come from a query string.

При работе с CompatibilityVersion.Version_2_1 или более поздней версией проверка узла верхнего уровня по умолчанию включена.When running with CompatibilityVersion.Version_2_1 or later, top-level node validation is enabled by default. В противном случае проверка узла верхнего уровня отключена.Otherwise, top-level node validation is disabled. Параметр по умолчанию можно переопределить, задав свойство AllowValidatingTopLevelNodes в (Startup.ConfigureServices), как показано ниже.The default option can be overridden by setting the AllowValidatingTopLevelNodes property in (Startup.ConfigureServices), as shown here:

services.AddMvc(options => 
    {
        options.MaxModelValidationErrors = 50;
        options.AllowValidatingTopLevelNodes = false;
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Максимальное количество ошибокMaximum errors

При достижении максимального количества ошибок (по умолчанию 200) проверка прекращается.Validation stops when the maximum number of errors is reached (200 by default). Это число можно изменить с помощью следующего кода в Startup.ConfigureServices:You can configure this number with the following code in Startup.ConfigureServices:

services.AddMvc(options => 
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            (_) => "The field is required.");
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton
    <IValidationAttributeAdapterProvider, 
     CustomValidationAttributeAdapterProvider>();

Максимальная рекурсияMaximum recursion

ValidationVisitor проходит через граф объектов в проверяемой модели.ValidationVisitor traverses the object graph of the model being validated. У моделей, которые очень глубоки или содержат бесконечную рекурсию, в ходе проверки может произойти переполнение стека.For models that are very deep or are infinitely recursive, validation may result in stack overflow. MvcOptions.MaxValidationDepth предоставляет способ остановить проверку до превышения настроенной глубины рекурсии обхода.MvcOptions.MaxValidationDepth provides a way to stop validation early if the visitor recursion exceeds a configured depth. Значение MvcOptions.MaxValidationDepth по умолчанию — 32 при работе в CompatibilityVersion.Version_2_2 или более поздней версии.The default value of MvcOptions.MaxValidationDepth is 32 when running with CompatibilityVersion.Version_2_2 or later. Для более ранних версий значение равно NULL; это означает отсутствие ограничения глубины.For earlier versions, the value is null, which means no depth constraint.

Автоматическое сокращениеAutomatic short-circuit

Проверка автоматически сокращается (пропускается), если граф модели не требует проверки.Validation is automatically short-circuited (skipped) if the model graph doesn't require validation. К числу объектов, которые среда выполнения пропускает при проверке, относятся коллекции примитивов (такие как byte[], string[], Dictionary<string, string>) и сложные графы объектов, которые не имеют проверяющих элементов управления.Objects that the runtime skips validation for include collections of primitives (such as byte[], string[], Dictionary<string, string>) and complex object graphs that don't have any validators.

Отключение проверкиDisable validation

Чтобы отключить проверку, сделайте следующее.To disable validation:

  1. Создайте реализацию интерфейса IObjectModelValidator, которая не помечает поля как недопустимые.Create an implementation of IObjectModelValidator that doesn't mark any fields as invalid.

    public class NullObjectModelValidator : IObjectModelValidator
    {
        public void Validate(
            ActionContext actionContext,
            ValidationStateDictionary validationState,
            string prefix,
            object model)
        {
        }
    }
    
  2. Добавьте следующий код в Startup.ConfigureServices для замены реализации IObjectModelValidator по умолчанию в контейнере внедрения зависимостей.Add the following code to Startup.ConfigureServices to replace the default IObjectModelValidator implementation in the dependency injection container.

    // There is only one `IObjectModelValidator` object,
    // so AddSingleton replaces the default one.
    services.AddSingleton<IObjectModelValidator>(new NullObjectModelValidator());
    

По-прежнему могут отображаться ошибки состояния модели, которые идут из привязки модели.You might still see model state errors that originate from model binding.

Проверка на стороне клиентаClient-side validation

Проверка на стороне клиента не позволяет отправлять форму, пока ее данные не будут допустимыми.Client-side validation prevents submission until the form is valid. При нажатии кнопки "Отправить" выполняется код JavaScript, который либо отправляет форму, либо выводит сообщения об ошибках.The Submit button runs JavaScript that either submits the form or displays error messages.

Проверка на стороне клиента позволяет избежать ненужного кругового захода на сервер при наличии ошибки ввода в форме.Client-side validation avoids an unnecessary round trip to the server when there are input errors on a form. Ссылки на следующий скрипт в _Layout.cshtml и _ValidationScriptsPartial.cshtml поддерживают проверку на стороне клиента.The following script references in _Layout.cshtml and _ValidationScriptsPartial.cshtml support client-side validation:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>

Скрипт ненавязчивой проверки jQuery — это настраиваемая интерфейсная библиотека Майкрософт, которая основана на популярном подключаемом модуле jQuery Validate.The jQuery Unobtrusive Validation script is a custom Microsoft front-end library that builds on the popular jQuery Validate plugin. Без скрипта ненавязчивой проверки jQuery одну и ту же логику проверки приходилось бы реализовывать в двух местах: в атрибутах проверки для свойств модели на стороне сервера, а затем еще раз в скриптах на стороне клиента.Without jQuery Unobtrusive Validation, you would have to code the same validation logic in two places: once in the server-side validation attributes on model properties, and then again in client-side scripts. Вместо этого вспомогательные функции тегов и вспомогательные функции HTML могут использовать атрибуты проверки и метаданные типов из свойств модели для обработки атрибутов data- HTML 5 в элементах форм, требующих проверки.Instead, Tag Helpers and HTML helpers use the validation attributes and type metadata from model properties to render HTML 5 data- attributes for the form elements that need validation. Скрипт ненавязчивой проверки jQuery анализирует эти атрибуты data- и передает логику в подключаемый модуль jQuery Validate, по сути копируя логику проверки на стороне сервера в клиент.jQuery Unobtrusive Validation parses the data- attributes and passes the logic to jQuery Validate, effectively "copying" the server-side validation logic to the client. Ошибки проверки могут выводиться в клиенте с помощью вспомогательных функций тегов, как показано ниже.You can display validation errors on the client using tag helpers as shown here:

<div class="form-group">
    <label asp-for="ReleaseDate" class="col-md-2 control-label"></label>
    <div class="col-md-10">
        <input asp-for="ReleaseDate" class="form-control" />
        <span asp-validation-for="ReleaseDate" class="text-danger"></span>
    </div>
</div>

Приведенные выше вспомогательные функции тегов отрисовывают следующий код HTML.The preceding tag helpers render the following HTML.

<form action="/Movies/Create" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <div class="text-danger"></div>
        <div class="form-group">
            <label class="col-md-2 control-label" for="ReleaseDate">ReleaseDate</label>
            <div class="col-md-10">
                <input class="form-control" type="datetime"
                data-val="true" data-val-required="The ReleaseDate field is required."
                id="ReleaseDate" name="ReleaseDate" value="">
                <span class="text-danger field-validation-valid"
                data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
            </div>
        </div>
    </div>
</form>

Обратите внимание на то, что атрибуты data- в выходных данных HTML соответствуют атрибутам проверки для свойства ReleaseDate.Notice that the data- attributes in the HTML output correspond to the validation attributes for the ReleaseDate property. Атрибут data-val-required содержит сообщение об ошибке, которое выводится, если пользователь не заполнил поле даты выхода.The data-val-required attribute contains an error message to display if the user doesn't fill in the release date field. Скрипт ненавязчивой проверки jQuery передает это значение в метод required() подключаемого модуля jQuery Validate, который затем выводит это сообщение в соответствующем элементе <span> .jQuery Unobtrusive Validation passes this value to the jQuery Validate required() method, which then displays that message in the accompanying <span> element.

Проверка типа данных основана на типе свойства в .NET, если его не переопределяет атрибут [DataType].Data type validation is based on the .NET type of a property, unless that is overridden by a [DataType] attribute. Браузеры имеют свои сообщения об по умолчанию, но пакет ненавязчивой проверки jQuery может переопределять эти сообщения.Browsers have their own default error messages, but the jQuery Validation Unobtrusive Validation package can override those messages. Атрибуты и подклассы [DataType], такие как [EmailAddress], позволяют указать сообщение об ошибке.[DataType] attributes and subclasses such as [EmailAddress] let you specify the error message.

Добавление проверки к динамическим формамAdd Validation to Dynamic Forms

Скрипт ненавязчивой проверки jQuery передает логику и параметры проверки в подключаемый модуль jQuery Validate при первой загрузке страницы.jQuery Unobtrusive Validation passes validation logic and parameters to jQuery Validate when the page first loads. Поэтому динамически создаваемые формы не подвергаются проверке автоматически.Therefore, validation doesn't work automatically on dynamically generated forms. Чтобы включить проверку, необходимо указать, что скрипт ненавязчивой проверки jQuery должен анализировать динамическую форму сразу после ее создания.To enable validation, tell jQuery Unobtrusive Validation to parse the dynamic form immediately after you create it. Например, приведенный ниже код показывает, как можно настроить проверку на стороне клиента для формы, добавленной посредством AJAX.For example, the following code sets up client-side validation on a form added via AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Метод $.validator.unobtrusive.parse() принимает селектор jQuery в качестве единственного аргумента.The $.validator.unobtrusive.parse() method accepts a jQuery selector for its one argument. Этот метод предписывает скрипту ненавязчивой проверки jQuery анализировать атрибуты data- форм в этом селекторе.This method tells jQuery Unobtrusive Validation to parse the data- attributes of forms within that selector. Значения этих атрибутов затем передаются в подключаемый модуль jQuery Validate.The values of those attributes are then passed to the jQuery Validate plugin.

Добавление проверки к динамическим элементам управленияAdd Validation to Dynamic Controls

Метод $.validator.unobtrusive.parse() обрабатывает всю форму, а не отдельные динамически создаваемые элементы управления, такие как <input> и <select/>.The $.validator.unobtrusive.parse() method works on an entire form, not on individual dynamically generated controls, such as <input> and <select/>. Для повторной обработки формы удалите данные проверки, которые были добавлены при анализе формы ранее, как показано в следующем примере:To reparse the form, remove the validation data that was added when the form was parsed earlier, as shown in the following example:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validate
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Настраиваемая проверка на стороне клиентаCustom client-side validation

Настраиваемая проверка на стороне клиента выполняется путем создания атрибутов HTML data-, которые работают с настраиваемым адаптером проверки jQuery.Custom client-side validation is done by generating data- HTML attributes that work with a custom jQuery Validate adapter. В следующем примере кода адаптера используются атрибуты ClassicMovie и ClassicMovie2, которые были введены ранее в этой статье.The following sample adapter code was written for the ClassicMovie and ClassicMovie2 attributes that were introduced earlier in this article:

$.validator.addMethod('classicmovie',
    function (value, element, params) {
        // Get element value. Classic genre has value '0'.
        var genre = $(params[0]).val(),
            year = params[1],
            date = new Date(value);
        if (genre && genre.length > 0 && genre[0] === '0') {
            // Since this is a classic movie, invalid if release date is after given year.
            return date.getUTCFullYear() <= year;
        }

        return true;
    });

$.validator.unobtrusive.adapters.add('classicmovie',
    ['year'],
    function (options) {
        var element = $(options.form).find('select#Genre')[0];
        options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
        options.messages['classicmovie'] = options.message;
    });

Сведения о способах создания адаптеров см. в документации по проверкам jQuery.For information about how to write adapters, see the jQuery Validate documentation.

Использование адаптера для заданного поля инициируется атрибутами data-, которые:The use of an adapter for a given field is triggered by data- attributes that:

  • помечают поле как проходящее проверку (data-val="true");Flag the field as being subject to validation (data-val="true").
  • указывают имя правила проверки и текст сообщения об ошибке (например, data-val-rulename="Error message.");Identify a validation rule name and error message text (for example, data-val-rulename="Error message.").
  • указывают любые дополнительные параметры, в которых нуждается проверяющий элемент управления (например, data-val-rulename-parm1="value").Provide any additional parameters the validator needs (for example, data-val-rulename-parm1="value").

В следующем примере показаны атрибуты data- для нашего атрибута ClassicMovie из примера приложения.The following example shows the data- attributes for the sample app's ClassicMovie attribute:

<input class="form-control" type="datetime"
    data-val="true"
    data-val-classicmovie1="Classic movies must have a release year earlier than 1960."
    data-val-classicmovie1-year="1960"
    data-val-required="The ReleaseDate field is required."
    id="ReleaseDate" name="ReleaseDate" value="">

Как отмечалось ранее, вспомогательные функции тегов и вспомогательные методы HTML используют сведения из атрибутов проверки для подготовки к отрисовке атрибутов data-.As noted earlier, Tag Helpers and HTML helpers use information from validation attributes to render data- attributes. Существует два варианта для написания кода, который приводит к созданию настраиваемых атрибутов HTML data-.There are two options for writing code that results in the creation of custom data- HTML attributes:

  • Создайте класс, производный от AttributeAdapterBase<TAttribute>, и класс, реализующий IValidationAttributeAdapterProvider; зарегистрируйте атрибут и его адаптер в функции внедрения зависимостей.Create a class that derives from AttributeAdapterBase<TAttribute> and a class that implements IValidationAttributeAdapterProvider, and register your attribute and its adapter in DI. Этот метод следует за участником с единственной обязанностью в том, что код проверки, связанный с сервером и клиентом, выделен в отдельные классы.This method follows the single responsibility principal in that server-related and client-related validation code is in separate classes. Адаптер также имеет следующее преимущество: так как он зарегистрирован в функции внедрения зависимостей, для него при необходимости доступны другие службы в ней.The adapter also has the advantage that since it is registered in DI, other services in DI are available to it if needed.
  • Реализуйте IClientModelValidator в вашем классе ValidationAttribute.Implement IClientModelValidator in your ValidationAttribute class. Этот метод стоит использовать, если атрибут не выполняет проверку на стороне сервера и не нуждается в службах из функции внедрения зависимостей.This method might be appropriate if the attribute doesn't do any server-side validation and doesn't need any services from DI.

AttributeAdapter для проверки на стороне клиентаAttributeAdapter for client-side validation

Этот метод отрисовки атрибутов data- в формате HTML используется атрибутом ClassicMovie в примере приложения.This method of rendering data- attributes in HTML is used by the ClassicMovie attribute in the sample app. Чтобы добавить клиентскую проверку с помощью этого метода, сделайте следующее.To add client validation by using this method:

  1. Создайте класс адаптера атрибута для настраиваемого атрибута проверки.Create an attribute adapter class for the custom validation attribute. Наследуйте класс от AttributeAdapterBase<T>.Derive the class from AttributeAdapterBase<T>. Создайте метод AddValidation, который добавляет атрибуты data- для выводимых данных, как показано в следующем примере.Create an AddValidation method that adds data- attributes to the rendered output, as shown in this example:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        private int _year;
    
        public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute, IStringLocalizer stringLocalizer) : base (attribute, stringLocalizer)
        {
            _year = attribute.Year;
        }
        public override void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
    
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
        {
            return Attribute.GetErrorMessage();
        }
    }
    
  2. Создайте класс поставщика адаптера, который реализует IValidationAttributeAdapterProvider.Create an adapter provider class that implements IValidationAttributeAdapterProvider. В методе GetAttributeAdapter передайте настраиваемый атрибут в конструктор адаптера, как показано в следующем примере.In the GetAttributeAdapter method pass in the custom attribute to the adapter's constructor, as shown in this example:

    public class CustomValidationAttributeAdapterProvider :
        IValidationAttributeAdapterProvider
    {
        IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
            IStringLocalizer stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
            else
            {
                return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
            }
        }
    }
    
  3. Зарегистрируйте поставщик адаптера для внедрения зависимостей в Startup.ConfigureServices.Register the adapter provider for DI in Startup.ConfigureServices:

    services.AddMvc(options => 
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                (_) => "The field is required.");
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddSingleton
        <IValidationAttributeAdapterProvider, 
         CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator для проверки на стороне клиентаIClientModelValidator for client-side validation

Этот метод отрисовки атрибутов data- в формате HTML используется атрибутом ClassicMovie2 в примере приложения.This method of rendering data- attributes in HTML is used by the ClassicMovie2 attribute in the sample app. Чтобы добавить клиентскую проверку с помощью этого метода, сделайте следующее.To add client validation by using this method:

  • В настраиваемом атрибуте проверки реализуйте интерфейс IClientModelValidator и создайте метод AddValidation.In the custom validation attribute, implement the IClientModelValidator interface and create an AddValidation method. В метод AddValidation добавьте атрибуты data- для проверки, как показано в следующем примере.In the AddValidation method, add data- attributes for validation, as shown in the following example:

    
    public class ClassicMovie2Attribute : ValidationAttribute, IClientModelValidator
    {
        private int _year;
    
        public ClassicMovie2Attribute(int year)
        {
            _year = year;
        }
    
        protected override ValidationResult IsValid(
            object value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > _year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
    
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = _year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
        protected string GetErrorMessage()
        {
            return $"Classic movies must have a release year no later than {_year} [from attribute 2].";
        }
    }
    

Отключение проверки на стороне клиентаDisable client-side validation

Следующий код отключает клиентскую проверку в представлениях MVC.The following code disables client validation in MVC views:

services.AddMvc().AddViewOptions(options =>
{
    if (_env.IsDevelopment())
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    }
});

В Razor Pages:And in Razor Pages:

services.Configure<HtmlHelperOptions>(o => o.ClientValidationEnabled = false);

Еще один вариант отключения клиентской проверки клиента — закомментировать ссылку на _ValidationScriptsPartial в вашем файле .cshtml.Another option for disabling client validation is to comment out the reference to _ValidationScriptsPartial in your .cshtml file.

Дополнительные ресурсыAdditional resources