Événement
Championnats du monde Power BI DataViz
14 févr., 16 h - 31 mars, 16 h
Avec 4 chances d’entrer, vous pourriez gagner un package de conférence et le rendre à la Live Grand Finale à Las Vegas
En savoir plusCe navigateur n’est plus pris en charge.
Effectuez une mise à niveau vers Microsoft Edge pour tirer parti des dernières fonctionnalités, des mises à jour de sécurité et du support technique.
Cet article explique comment valider l’entrée utilisateur dans ASP.NET Core MVC ou l’application Razor Pages.
Affichez ou téléchargez un exemple de code (procédure de téléchargement).
L’état du modèle représente les erreurs qui proviennent de deux sous-systèmes : liaison de modèle et validation de modèle. Les erreurs qui proviennent de la liaison de modèle sont généralement des erreurs de conversion de données. Par exemple, un « x » est entré dans un champ de nombres entiers. La validation du modèle se produit après la liaison de modèle et signale des erreurs lorsque les données ne sont pas conformes aux règles d’entreprise. Par exemple, un 0 est entré dans un champ qui attend une évaluation comprise entre 1 et 5.
La liaison et la validation de modèle se produisent avant l’exécution d’une action de contrôleur ou d’une méthode de gestionnaire Razor Pages. Pour les applications web, il incombe à l’application d’inspecter ModelState.IsValid
et de réagir de façon appropriée. Les applications web affichent généralement de nouveau la page avec un message d’erreur, comme illustré dans l’exemple Razor Pages suivant :
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Pour ASP.NET Core MVC avec des contrôleurs et des vues, l’exemple suivant montre comment vérifier ModelState.IsValid
dans une action de contrôleur :
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Les contrôleurs d’API web ne sont pas obligés de vérifier s’ils ont l’attribut ModelState.IsValid
[ApiController]. Dans ce cas, une réponse HTTP 400 automatique contenant les détails du problème est retournée si l’état du modèle n’est pas valide. Pour plus d’informations, consultez Réponses HTTP 400 automatiques.
La validation est automatique, mais vous souhaiterez peut-être la répéter manuellement. Par exemple, vous pourriez calculer une valeur pour une propriété, et souhaiter réexécuter la validation après avoir affecté la valeur calculée comme valeur de la propriété. Pour réexécuter la validation, appelez ModelStateDictionary.ClearValidationState pour effacer la validation spécifique au modèle en cours de validation, suivi de TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Les attributs de validation vous permettent de spécifier des règles de validation pour des propriétés de modèle. L’exemple suivant tiré de l’exemple d’application montre une classe de modèle annotée avec des attributs de validation. L’attribut [ClassicMovie]
est un attribut de validation personnalisé et les autres sont prédéfinis. [ClassicMovieWithClientValidator]
n’apparaît pas, cela montre une autre façon d’implémenter un attribut personnalisé.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Voici certains des attributs de validation prédéfinis :
[Required]
.[Remote]
Attribut.Vous trouverez la liste complète des attributs de validation dans l’System.ComponentModel.DataAnnotations espace de noms.
Les attributs de validation vous permettent de spécifier le message d’erreur à afficher pour l’entrée non valide. Par exemple :
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
En interne, les attributs appellent String.Format avec un espace réservé pour le nom de champ et parfois d’autres espaces réservés. Par exemple :
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Appliqué à une propriété Name
, le message d’erreur créé par le code précédent serait « Name length must be between 6 and 8 ».
Pour savoir quels paramètres sont passés à String.Format
pour le message d’erreur d’un attribut particulier, consultez le code source de DataAnnotations.
Par défaut, lorsqu’une erreur de validation se produit, la validation du modèle génère un ModelStateDictionary avec le nom de propriété comme clé d’erreur. Certaines applications, comme les applications monopages, tirent parti de l’utilisation de noms de propriétés JSON pour les erreurs de validation générées à partir des API web. Le code suivant configure la validation pour qu’elle utilise les noms de propriété JSON dans le format SystemTextJsonValidationMetadataProvider
:
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code suivant configure la validation afin qu’elle utilise le NewtonsoftJsonValidationMetadataProvider
, permettant ainsi l’utilisation des noms de propriétés JSON lors de l’emploi de Json.NET :
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour obtenir un exemple de stratégie d’utilisation de camel-casing, consultez Program.cs
sur GitHub.
Par défaut, le système de validation traite les propriétés ou paramètres n’acceptant pas les valeurs Null comme s’ils avaient un attribut [Required(AllowEmptyStrings = true)]
. En activant Nullable
les contextes, MVC commence implicitement à valider les propriétés ou paramètres non null comme s’ils avaient l’attribut [Required(AllowEmptyStrings = true)]
. Prenez le code suivant :
public class Person
{
public string Name { get; set; }
}
Si l’application a été générée avec <Nullable>enable</Nullable>
, une valeur manquante pour Name
dans une publication JSON ou un formulaire entraîne une erreur de validation. Cela peut sembler contradictoire puisque l’attribut [Required(AllowEmptyStrings = true)]
est implicite, mais ce comportement est attendu, car chaînes vides sont converties en null par défaut. Utilisez un type de référence Null pour autoriser la spécification de valeurs Null ou manquantes pour la propriété Name
:
public class Person
{
public string? Name { get; set; }
}
Ce comportement peut être désactivé en configurant SuppressImplicitRequiredAttributeForNonNullableReferenceTypes dans Program.cs
:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Sur le serveur, une valeur obligatoire est considérée comme manquante si la propriété est Null. Un champ n’acceptant pas les valeurs Null est toujours valide, et le message d’erreur de l’attribut [Required]
n’est jamais affiché.
Toutefois, la liaison de modèle pour une propriété n’acceptant pas les valeurs Null peut échouer, entraînant l’affichage d’un message d’erreur tel que The value '' is invalid
. Pour spécifier un message d’erreur personnalisé pour la validation côté serveur des types n’acceptant pas les valeurs Null, vous disposez des options suivantes :
Rendre le champ Nullable (par exemple, decimal?
au lieu de decimal
). Les types valeur Null<T> sont traités comme des types Null standard.
Spécifier le message d’erreur par défaut devant être utilisé par la liaison de modèle, comme indiqué dans l’exemple suivant :
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Pour plus d’informations sur les erreurs de liaison de modèle pour lesquelles vous pouvez définir des messages par défaut, consultez DefaultModelBindingMessageProvider.
Les chaînes et les types n’acceptant pas les valeurs Null sont gérés différemment sur le client et sur le serveur. Sur le client :
Comme indiqué précédemment, les types n’acceptant pas les valeurs Null sont traités comme s’ils avaient un attribut [Required(AllowEmptyStrings = true)]
. Cela signifie que vous bénéficiez d’une validation côté client même si vous n’appliquez pas l’attribut [Required(AllowEmptyStrings = true)]
. En revanche, si vous n’utilisez pas l’attribut, vous recevez un message d’erreur par défaut. Pour spécifier un message d’erreur personnalisé, utilisez l’attribut.
L’attribut [Remote] implémente la validation côté client qui nécessite l’appel d’une méthode sur le serveur pour déterminer si le champ d’entrée est valide. Par exemple, l’application peut avoir besoin de vérifier si un nom d’utilisateur est déjà en cours d’utilisation.
Pour implémenter la validation à distance
Créez une méthode d’action devant être appelée par JavaScript. La méthode de validation jQuery remote attend une réponse JSON :
true
signifie que les données d’entrée sont valides.false
, undefined
ou null
signifie que l’entrée n’est pas valide. Affichez le message d’erreur par défaut.Voici un exemple de méthode d’action qui retourne un message d’erreur personnalisé :
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
Dans la classe de modèle, annotez la propriété avec un attribut [Remote]
qui pointe vers la méthode d’action de validation, comme indiqué dans l’exemple suivant :
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; } = null!;
La validation côté serveur doit également être implémentée pour les clients qui ont désactivé JavaScript.
La propriété AdditionalFields de l’attribut [Remote]
vous permet de valider des combinaisons de champs relativement à des données présentes sur le serveur. Par exemple, si le modèle User
a des propriétés FirstName
et LastName
, vous pouvez vérifier qu’aucun utilisateur existant n’a déjà cette paire de noms. L'exemple suivant montre comment utiliser AdditionalFields
:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;
AdditionalFields
peut être défini explicitement sur les chaînes «Prénom» et «Nom», mais utiliser le nom de l’opérateur simplifie la refactorisation ultérieure. La méthode d’action pour cette validation doit accepter à la fois les arguments de firstName
et lastName
:
[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);
}
Quand l’utilisateur entre un nom ou un prénom, JavaScript effectue un appel à distance pour vérifier si cette paire de noms est déjà utilisée.
Pour valider deux champs supplémentaires ou plus, spécifiez-les sous la forme d’une liste délimitée par des virgules. Par exemple, pour ajouter une propriété MiddleName
au modèle, définissez l’attribut [Remote]
comme indiqué dans l’exemple suivant :
[Remote(action: "VerifyName", controller: "Users",
AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields
, comme tous les arguments d’attribut, doit être une expression constante. Vous ne devez donc pas utiliser une chaîne interpolée ou appeler Join pour initialiser AdditionalFields
.
Si vous avez besoin d’une validation non fournie par les attributs prédéfinis, vous pouvez :
Pour les scénarios non gérés par les attributs de validation prédéfinis, vous pouvez créer des attributs de validation personnalisés. Créez une classe qui hérite de ValidationAttribute et substituez la méthode IsValid.
La méthode IsValid
accepte un objet nommé value, qui est l’entrée à valider. Une surcharge accepte également un objet ValidationContext, qui fournit des informations supplémentaires telles que l’instance de modèle créée par la liaison de modèle.
L’exemple suivant vérifie que la date de sortie d’un film appartenant au genre Classic n’est pas ultérieure à une année spécifiée. L’attribut [ClassicMovie]
:
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;
}
}
La variable movie
de l’exemple précédent représente un objet Movie
qui contient les données de l’envoi du formulaire. Quand la validation échoue, un ValidationResult avec un message d’erreur est retourné.
L’exemple précédent fonctionne uniquement avec les types Movie
. Une autre option de validation au niveau de la classe consiste à implémenter IValidatableObject dans la classe de modèle, comme indiqué dans l’exemple suivant :
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[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) });
}
}
}
Le code suivant montre comment ajouter une erreur de modèle après avoir examiné le modèle :
if (Contact.Name == Contact.ShortName)
{
ModelState.AddModelError("Contact.ShortName",
"Short name can't be the same as Name.");
}
Le code suivant implémente le test de validation dans un contrôleur :
if (contact.Name == contact.ShortName)
{
ModelState.AddModelError(nameof(contact.ShortName),
"Short name can't be the same as Name.");
}
Le code suivant vérifie que le numéro de téléphone et l’adresse e-mail sont uniques :
public async Task<IActionResult> OnPostAsync()
{
// Attach Validation Error Message to the Model on validation failure.
if (Contact.Name == Contact.ShortName)
{
ModelState.AddModelError("Contact.ShortName",
"Short name can't be the same as Name.");
}
if (_context.Contact.Any(i => i.PhoneNumber == Contact.PhoneNumber))
{
ModelState.AddModelError("Contact.PhoneNumber",
"The Phone number is already in use.");
}
if (_context.Contact.Any(i => i.Email == Contact.Email))
{
ModelState.AddModelError("Contact.Email", "The Email is already in use.");
}
if (!ModelState.IsValid || _context.Contact == null || Contact == null)
{
// if model is invalid, return the page with the model state errors.
return Page();
}
_context.Contact.Add(Contact);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Le code suivant implémente le test de validation dans un contrôleur :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,ShortName,Email,PhoneNumber")] Contact contact)
{
// Attach Validation Error Message to the Model on validation failure.
if (contact.Name == contact.ShortName)
{
ModelState.AddModelError(nameof(contact.ShortName),
"Short name can't be the same as Name.");
}
if (_context.Contact.Any(i => i.PhoneNumber == contact.PhoneNumber))
{
ModelState.AddModelError(nameof(contact.PhoneNumber),
"The Phone number is already in use.");
}
if (_context.Contact.Any(i => i.Email == contact.Email))
{
ModelState.AddModelError(nameof(contact.Email), "The Email is already in use.");
}
if (ModelState.IsValid)
{
_context.Add(contact);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(contact);
}
La vérification d’un numéro de téléphone ou d’un e-mail unique est généralement également effectuée avec la validation à distance.
Considérez la classe ValidateNameAttribute
personnalisée suivante :
public class ValidateNameAttribute : ValidationAttribute
{
public ValidateNameAttribute()
{
const string defaultErrorMessage = "Error with Name";
ErrorMessage ??= defaultErrorMessage;
}
protected override ValidationResult? IsValid(object? value,
ValidationContext validationContext)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return new ValidationResult("Name is required.");
}
if (value.ToString()!.ToLower().Contains("zz"))
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
}
Dans le code suivant, l’attribut [ValidateName]
personnalisé est appliqué :
public class Contact
{
public Guid Id { get; set; }
[ValidateName(ErrorMessage = "Name must not contain `zz`")]
public string? Name { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
}
Lorsque le modèle contient zz
, un nouveau ValidationResult est retourné.
Les nœuds de niveau supérieur incluent les éléments suivants :
Les nœuds de niveau supérieur liés au modèle sont validés en plus de la validation des propriétés du modèle. Dans l’exemple suivant tiré de l’exemple d’application, la méthodeVerifyPhone
utilise RegularExpressionAttribute pour valider le paramètre d’action phone
:
[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);
}
Les nœuds de niveau supérieur peuvent utiliser BindRequiredAttribute avec des attributs de validation. Dans l’exemple suivant de l’exemple d’application, la méthodeCheckAge
spécifie que le paramètre age
doit être lié à partir de la chaîne de requête au moment de l’envoi du formulaire :
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
Dans la page de vérification de l’âge (CheckAge.cshtml
), il y a deux formulaires. Le premier formulaire envoie une valeur Age
égale à 99
en tant que chaîne de requête : https://localhost:5001/Users/CheckAge?Age=99
.
Quand un paramètre age
au format approprié est envoyé à partir de la chaîne de requête, le formulaire est validé.
Le second formulaire de la page de vérification de l’âge envoie la valeur Age
dans le corps de la requête, ce qui entraîne un échec de la validation. L’échec de la liaison est dû au fait que le paramètre age
doit provenir d’une chaîne de requête.
La validation s’arrête quand le nombre maximal d’erreurs est atteint (200 par défaut). Vous pouvez configurer ce nombre avec le code suivant dans Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
ValidationVisitor parcourt le graphe d’objet du modèle en cours de validation. Pour les modèles très profonds ou infiniment récursifs, la validation peut entraîner un dépassement de la capacité de la pile. MvcOptions.MaxValidationDepth fournit un moyen d’interrompre la validation de manière anticipée si la récursivité du visiteur dépasse une profondeur configurée. La valeur par défaut de MvcOptions.MaxValidationDepth
est 32.
La validation est automatiquement court-circuitée (ignorée) si le graphe du modèle ne nécessite pas de validation. Les objets pour lesquels le runtime ignore la validation comprennent les collections de primitives (telles que byte[]
, string[]
, Dictionary<string, string>
) et les graphes d’objets complexes qui n’ont pas de validateur.
La validation côté client empêche l’envoi jusqu’à ce que le formulaire soit valide. Le bouton Submit exécute le code JavaScript qui envoie le formulaire ou qui affiche des messages d’erreur.
La validation côté client permet d’éviter un aller-retour inutile vers le serveur quand il existe des erreurs d’entrée sur un formulaire. Les références de script suivantes dans _Layout.cshtml
et _ValidationScriptsPartial.cshtml
valident du côté du client:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>
Le script jQuery Unobtrusive Validation est une bibliothèque frontale personnalisée de Microsoft qui s’appuie sur le plug-in bien connu jQuery Validate. Sans jQuery Unobtrusive Validation, vous devriez coder la même logique de validation à deux endroits : une fois dans les attributs de validation côté serveur sur les propriétés du modèle, puis à nouveau dans les scripts côté client. Au lieu de cela, les Tag Helpers et les helpers HTML utilisent les attributs de validation et les métadonnées de type des propriétés du modèle afin de restituer les attributs data-
HTML 5 pour les éléments de formulaire nécessitant une validation. La validation non obstrustice jQuery analyse les attributs data-
et transmet la logique à jQuery Validate, en « copiant » la logique de validation côté serveur vers le client. Vous pouvez afficher les erreurs de validation sur le client en utilisant des Tag Helpers, comme indiqué ici :
<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>
Les assistants de balisage précédents restituent le code HTML suivant :
<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>
Notez que les attributs data-
dans la sortie HTML correspondent aux attributs de validation pour la propriété Movie.ReleaseDate
. L’attribut data-val-required
contient un message d’erreur à afficher si l’utilisateur ne renseigne pas le champ correspondant à la date de sortie. La validation non obstructive jQuery transmet cette valeur à la méthode de validation jQuery requise, qui affiche alors ce message dans l’élément <span > qui l’accompagne.
La validation du type de données est basée sur le type .NET d’une propriété, sauf en cas de substitution par un attribut [DataType] . Les navigateurs ont leurs propres messages d’erreur par défaut, mais le package jQuery Validation Unobtrusive Validation peut remplacer ces messages. Les attributs [DataType]
et les sous-classes comme [EmailAddress] permettent de spécifier le message d’erreur.
Pour plus d’informations sur la validation non obstructive, consultez ce problème GitHub.
La validation jQuery non obstructive transmet la logique et les paramètres de validation à jQuery Validate lors du premier chargement de la page. Par conséquent, la validation ne fonctionne pas automatiquement sur les formulaires générés de manière dynamique. Pour activer la validation, vous devez faire en sorte que jQuery Validate analyse le formulaire dynamique immédiatement après l’avoir créé. Par exemple, le code suivant définit la validation côté client sur un formulaire ajouté par le biais d’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);
}
})
La méthode $.validator.unobtrusive.parse()
accepte un sélecteur jQuery comme argument. Cette méthode indique à jQuery Unobtrusive Validation d’analyser les attributs data-
des formulaires dans ce sélecteur. Les valeurs de ces attributs sont ensuite transmises au plug-in jQuery Validate.
La méthode $.validator.unobtrusive.parse()
opère sur un formulaire entier, et non sur des contrôles individuels générés de manière dynamique tels que <input>
et <select/>
. Pour réanalyser le formulaire, supprimez les données de validation qui ont été ajoutées quand le formulaire a été analysé précédemment, comme illustré dans l’exemple suivant :
$.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 Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
La validation personnalisée côté client s’effectue en générant des attributs HTML data-
qui fonctionnent avec un adaptateur jQuery Validate personnalisé. L’exemple de code d’adaptateur suivant a été écrit pour les attributs [ClassicMovie]
et [ClassicMovieWithClientValidator]
qui ont été introduits plus haut dans cet 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;
});
Pour plus d’informations sur la façon d’écrire des adaptateurs, consultez la documentation de jQuery Validate.
L’utilisation d’un adaptateur pour un champ donné est déclenchée par des attributs data-
qui :
data-val="true"
).data-val-rulename="Error message."
).data-val-rulename-param1="value"
).L’exemple suivant montre les attributs data-
pour l’attribut de l’exemple d’application ClassicMovie
:
<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="">
Comme mentionné plus haut, les Tag Helpers et les helpers HTML utilisent les informations des attributs de validation pour restituer les attributs data-
. Il existe deux options pour l’écriture de code qui entraîne la création d’attributs HTML data-
personnalisés :
Cette méthode de rendu des attributs data-
en HTML est utilisée par l’attribut ClassicMovie
dans l’exemple d’application. Pour ajouter la validation côté client à l’aide de cette méthode
Créez une classe d’adaptateurs d’attributs pour l’attribut de validation personnalisé. Dérivez la classe de AttributeAdapterBase<TAttribute>. Créez une méthode AddValidation
qui ajoute des attributs data-
à la sortie restituée, comme illustré dans cet exemple :
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();
}
Créez une classe de fournisseurs d’adaptateurs qui implémente IValidationAttributeAdapterProvider. Dans la méthode GetAttributeAdapter, passez l’attribut personnalisé au constructeur de l’adaptateur, comme illustré dans cet exemple :
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);
}
}
Inscrivez le fournisseur d’adaptateurs auprès de l’injection de dépendances dans Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Cette méthode de rendu des attributs data-
en HTML est utilisée par l’attribut ClassicMovieWithClientValidator
dans l’exemple d’application. Pour ajouter la validation côté client à l’aide de cette méthode
Dans l’attribut de validation personnalisé, implémentez l’interface IClientModelValidator et créez une méthode AddValidation. Dans la méthode AddValidation
, ajoutez des attributs data-
pour la validation, comme indiqué dans l’exemple suivant :
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 static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
Le code suivant désactive la validation côté client dans Razor Pages :
builder.Services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Autres options pour désactiver la validation côté client :
_ValidationScriptsPartial
dans tous les fichiers .cshtml
.L’approche précédente n’empêche pas la validation côté client de la bibliothèque de classes IdentityRazor ASP.NET Core. Pour plus d’informations, consultez Génération de modèles automatiqueIdentity dans les projets ASP.NET Core.
Les détails du problème ne sont pas le seul format de réponse à décrire une erreur d’API HTTP. Toutefois, ils sont couramment utilisés pour signaler des erreurs pour les API HTTP.
Le service des détails du problème implémente l’interface IProblemDetailsService, qui prend en charge la création de détails de problème dans ASP.NET Core. La méthode d’extension AddProblemDetails(IServiceCollection) sur IServiceCollection enregistre l’implémentation IProblemDetailsService
par défaut.
Dans les applications ASP.NET Core, l’intergiciel suivant génère des réponses HTTP sur les détails de problème lorsque AddProblemDetails
est appelé, sauf si l’en-tête HTTP de requêteAccept
n’inclut pas l’un des types de contenu pris en charge par le IProblemDetailsWriter inscrit (par défaut : application/json
) :
Accept
n’inclut pas text/html
.Cet article explique comment valider l’entrée utilisateur dans ASP.NET Core MVC ou l’application Razor Pages.
Affichez ou téléchargez un exemple de code (procédure de téléchargement).
L’état du modèle représente les erreurs qui proviennent de deux sous-systèmes : liaison de modèle et validation de modèle. Les erreurs qui proviennent de la liaison de modèle sont généralement des erreurs de conversion de données. Par exemple, un « x » est entré dans un champ de nombres entiers. La validation du modèle se produit après la liaison de modèle et signale des erreurs lorsque les données ne sont pas conformes aux règles d’entreprise. Par exemple, un 0 est entré dans un champ qui attend une évaluation comprise entre 1 et 5.
La liaison et la validation de modèle se produisent avant l’exécution d’une action de contrôleur ou d’une méthode de gestionnaire Razor Pages. Pour les applications web, il incombe à l’application d’inspecter ModelState.IsValid
et de réagir de façon appropriée. Les applications web affichent généralement de nouveau la page avec un message d’erreur, comme illustré dans l’exemple Razor Pages suivant :
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Pour ASP.NET Core MVC avec des contrôleurs et des vues, l’exemple suivant montre comment vérifier ModelState.IsValid
dans une action de contrôleur :
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Les contrôleurs d’API web ne sont pas obligés de vérifier s’ils ont l’attribut ModelState.IsValid
[ApiController]. Dans ce cas, une réponse HTTP 400 automatique contenant les détails du problème est retournée si l’état du modèle n’est pas valide. Pour plus d’informations, consultez Réponses HTTP 400 automatiques.
La validation est automatique, mais vous souhaiterez peut-être la répéter manuellement. Par exemple, vous pourriez calculer une valeur pour une propriété, et souhaiter réexécuter la validation après avoir affecté la valeur calculée comme valeur de la propriété. Pour réexécuter la validation, appelez ModelStateDictionary.ClearValidationState pour effacer la validation spécifique au modèle en cours de validation, suivi de TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Les attributs de validation vous permettent de spécifier des règles de validation pour des propriétés de modèle. L’exemple suivant tiré de l’exemple d’application montre une classe de modèle annotée avec des attributs de validation. L’attribut [ClassicMovie]
est un attribut de validation personnalisé et les autres sont prédéfinis. [ClassicMovieWithClientValidator]
n’apparaît pas, cela montre une autre façon d’implémenter un attribut personnalisé.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Voici certains des attributs de validation prédéfinis :
[Required]
.[Remote]
Attribut.Vous trouverez la liste complète des attributs de validation dans l’System.ComponentModel.DataAnnotations espace de noms.
Les attributs de validation vous permettent de spécifier le message d’erreur à afficher pour l’entrée non valide. Par exemple :
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
En interne, les attributs appellent String.Format avec un espace réservé pour le nom de champ et parfois d’autres espaces réservés. Par exemple :
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Appliqué à une propriété Name
, le message d’erreur créé par le code précédent serait « Name length must be between 6 and 8 ».
Pour savoir quels paramètres sont passés à String.Format
pour le message d’erreur d’un attribut particulier, consultez le code source de DataAnnotations.
Par défaut, le système de validation traite les propriétés ou paramètres n’acceptant pas les valeurs Null comme s’ils avaient un attribut [Required(AllowEmptyStrings = true)]
. En activant Nullable
les contextes, MVC commence implicitement à valider les propriétés ou paramètres non Null des types non génériques ou de leurs paramètres comme s’ils avaient reçu l’attribut [Required(AllowEmptyStrings = true)]
. Prenez le code suivant :
public class Person
{
public string Name { get; set; }
}
Si l’application a été générée avec <Nullable>enable</Nullable>
, une valeur manquante pour Name
dans une publication JSON ou un formulaire entraîne une erreur de validation. Utilisez un type de référence Null pour autoriser la spécification de valeurs Null ou manquantes pour la propriété Name
:
public class Person
{
public string? Name { get; set; }
}
Ce comportement peut être désactivé en configurant SuppressImplicitRequiredAttributeForNonNullableReferenceTypes dans Program.cs
:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Les propriétés ne pouvant pas accepter la valeur Null sur les types génériques doivent inclure l’attribut [Required]
lorsque le type est requis. Dans le code suivant, TestRequired
n’est pas obligatoire :
public class WeatherForecast<T>
{
public string TestRequired { get; set; } = null!;
public T? Inner { get; set; }
}
Dans le code suivant, TestRequired
est explicitement marqué comme obligatoire :
using System.ComponentModel.DataAnnotations;
public class WeatherForecast<T>
{
[Required]
public string TestRequired { get; set; } = null!;
public T? Inner { get; set; }
}
Sur le serveur, une valeur obligatoire est considérée comme manquante si la propriété est Null. Un champ n’acceptant pas les valeurs Null est toujours valide, et le message d’erreur de l’attribut [Required]
n’est jamais affiché.
Toutefois, la liaison de modèle pour une propriété n’acceptant pas les valeurs Null peut échouer, entraînant l’affichage d’un message d’erreur tel que The value '' is invalid
. Pour spécifier un message d’erreur personnalisé pour la validation côté serveur des types n’acceptant pas les valeurs Null, vous disposez des options suivantes :
Rendre le champ Nullable (par exemple, decimal?
au lieu de decimal
). Les types valeur Null<T> sont traités comme des types Null standard.
Spécifier le message d’erreur par défaut devant être utilisé par la liaison de modèle, comme indiqué dans l’exemple suivant :
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Pour plus d’informations sur les erreurs de liaison de modèle pour lesquelles vous pouvez définir des messages par défaut, consultez DefaultModelBindingMessageProvider.
Les chaînes et les types n’acceptant pas les valeurs Null sont gérés différemment sur le client et sur le serveur. Sur le client :
Comme indiqué précédemment, les types n’acceptant pas les valeurs Null sont traités comme s’ils avaient un attribut [Required(AllowEmptyStrings = true)]
. Cela signifie que vous bénéficiez d’une validation côté client même si vous n’appliquez pas l’attribut [Required(AllowEmptyStrings = true)]
. En revanche, si vous n’utilisez pas l’attribut, vous recevez un message d’erreur par défaut. Pour spécifier un message d’erreur personnalisé, utilisez l’attribut.
L’attribut [Remote] implémente la validation côté client qui nécessite l’appel d’une méthode sur le serveur pour déterminer si le champ d’entrée est valide. Par exemple, l’application peut avoir besoin de vérifier si un nom d’utilisateur est déjà en cours d’utilisation.
Pour implémenter la validation à distance
Créez une méthode d’action devant être appelée par JavaScript. La méthode de validation jQuery remote attend une réponse JSON :
true
signifie que les données d’entrée sont valides.false
, undefined
ou null
signifie que l’entrée n’est pas valide. Affichez le message d’erreur par défaut.Voici un exemple de méthode d’action qui retourne un message d’erreur personnalisé :
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
Dans la classe de modèle, annotez la propriété avec un attribut [Remote]
qui pointe vers la méthode d’action de validation, comme indiqué dans l’exemple suivant :
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; } = null!;
La propriété AdditionalFields de l’attribut [Remote]
vous permet de valider des combinaisons de champs relativement à des données présentes sur le serveur. Par exemple, si le modèle User
a des propriétés FirstName
et LastName
, vous pouvez vérifier qu’aucun utilisateur existant n’a déjà cette paire de noms. L'exemple suivant montre comment utiliser AdditionalFields
:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;
AdditionalFields
peut être défini explicitement sur les chaînes «Prénom» et «Nom», mais utiliser le nom de l’opérateur simplifie la refactorisation ultérieure. La méthode d’action pour cette validation doit accepter à la fois les arguments de firstName
et lastName
:
[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);
}
Quand l’utilisateur entre un nom ou un prénom, JavaScript effectue un appel à distance pour vérifier si cette paire de noms est déjà utilisée.
Pour valider deux champs supplémentaires ou plus, spécifiez-les sous la forme d’une liste délimitée par des virgules. Par exemple, pour ajouter une propriété MiddleName
au modèle, définissez l’attribut [Remote]
comme indiqué dans l’exemple suivant :
[Remote(action: "VerifyName", controller: "Users",
AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields
, comme tous les arguments d’attribut, doit être une expression constante. Vous ne devez donc pas utiliser une chaîne interpolée ou appeler Join pour initialiser AdditionalFields
.
Si vous avez besoin d’une validation non fournie par les attributs prédéfinis, vous pouvez :
Pour les scénarios non gérés par les attributs de validation prédéfinis, vous pouvez créer des attributs de validation personnalisés. Créez une classe qui hérite de ValidationAttribute et substituez la méthode IsValid.
La méthode IsValid
accepte un objet nommé value, qui est l’entrée à valider. Une surcharge accepte également un objet ValidationContext, qui fournit des informations supplémentaires telles que l’instance de modèle créée par la liaison de modèle.
L’exemple suivant vérifie que la date de sortie d’un film appartenant au genre Classic n’est pas ultérieure à une année spécifiée. L’attribut [ClassicMovie]
:
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;
}
}
La variable movie
de l’exemple précédent représente un objet Movie
qui contient les données de l’envoi du formulaire. Quand la validation échoue, un ValidationResult avec un message d’erreur est retourné.
L’exemple précédent fonctionne uniquement avec les types Movie
. Une autre option de validation au niveau de la classe consiste à implémenter IValidatableObject dans la classe de modèle, comme indiqué dans l’exemple suivant :
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[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) });
}
}
}
Les nœuds de niveau supérieur incluent les éléments suivants :
Les nœuds de niveau supérieur liés au modèle sont validés en plus de la validation des propriétés du modèle. Dans l’exemple suivant tiré de l’exemple d’application, la méthodeVerifyPhone
utilise RegularExpressionAttribute pour valider le paramètre d’action phone
:
[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);
}
Les nœuds de niveau supérieur peuvent utiliser BindRequiredAttribute avec des attributs de validation. Dans l’exemple suivant de l’exemple d’application, la méthodeCheckAge
spécifie que le paramètre age
doit être lié à partir de la chaîne de requête au moment de l’envoi du formulaire :
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
Dans la page de vérification de l’âge (CheckAge.cshtml
), il y a deux formulaires. Le premier formulaire envoie une valeur Age
égale à 99
en tant que chaîne de requête : https://localhost:5001/Users/CheckAge?Age=99
.
Quand un paramètre age
au format approprié est envoyé à partir de la chaîne de requête, le formulaire est validé.
Le second formulaire de la page de vérification de l’âge envoie la valeur Age
dans le corps de la requête, ce qui entraîne un échec de la validation. L’échec de la liaison est dû au fait que le paramètre age
doit provenir d’une chaîne de requête.
La validation s’arrête quand le nombre maximal d’erreurs est atteint (200 par défaut). Vous pouvez configurer ce nombre avec le code suivant dans Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
ValidationVisitor parcourt le graphe d’objet du modèle en cours de validation. Pour les modèles très profonds ou infiniment récursifs, la validation peut entraîner un dépassement de la capacité de la pile. MvcOptions.MaxValidationDepth fournit un moyen d’interrompre la validation de manière anticipée si la récursivité du visiteur dépasse une profondeur configurée. La valeur par défaut de MvcOptions.MaxValidationDepth
est 32.
La validation est automatiquement court-circuitée (ignorée) si le graphe du modèle ne nécessite pas de validation. Les objets pour lesquels le runtime ignore la validation comprennent les collections de primitives (telles que byte[]
, string[]
, Dictionary<string, string>
) et les graphes d’objets complexes qui n’ont pas de validateur.
La validation côté client empêche l’envoi jusqu’à ce que le formulaire soit valide. Le bouton Submit exécute le code JavaScript qui envoie le formulaire ou qui affiche des messages d’erreur.
La validation côté client permet d’éviter un aller-retour inutile vers le serveur quand il existe des erreurs d’entrée sur un formulaire. Les références de script suivantes dans _Layout.cshtml
et _ValidationScriptsPartial.cshtml
valident du côté du client:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>
Le script jQuery Unobtrusive Validation est une bibliothèque frontale personnalisée de Microsoft qui s’appuie sur le plug-in bien connu jQuery Validate. Sans jQuery Unobtrusive Validation, vous devriez coder la même logique de validation à deux endroits : une fois dans les attributs de validation côté serveur sur les propriétés du modèle, puis à nouveau dans les scripts côté client. Au lieu de cela, les Tag Helpers et les helpers HTML utilisent les attributs de validation et les métadonnées de type des propriétés du modèle afin de restituer les attributs data-
HTML 5 pour les éléments de formulaire nécessitant une validation. La validation non obstrustice jQuery analyse les attributs data-
et transmet la logique à jQuery Validate, en « copiant » la logique de validation côté serveur vers le client. Vous pouvez afficher les erreurs de validation sur le client en utilisant des Tag Helpers, comme indiqué ici :
<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>
Les assistants de balisage précédents restituent le code HTML suivant :
<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>
Notez que les attributs data-
dans la sortie HTML correspondent aux attributs de validation pour la propriété Movie.ReleaseDate
. L’attribut data-val-required
contient un message d’erreur à afficher si l’utilisateur ne renseigne pas le champ correspondant à la date de sortie. La validation non obstructive jQuery transmet cette valeur à la méthode de validation jQuery requise, qui affiche alors ce message dans l’élément <span > qui l’accompagne.
La validation du type de données est basée sur le type .NET d’une propriété, sauf en cas de substitution par un attribut [DataType] . Les navigateurs ont leurs propres messages d’erreur par défaut, mais le package jQuery Validation Unobtrusive Validation peut remplacer ces messages. Les attributs [DataType]
et les sous-classes comme [EmailAddress] permettent de spécifier le message d’erreur.
Pour plus d’informations sur la validation non obstructive, consultez ce problème GitHub.
La validation jQuery non obstructive transmet la logique et les paramètres de validation à jQuery Validate lors du premier chargement de la page. Par conséquent, la validation ne fonctionne pas automatiquement sur les formulaires générés de manière dynamique. Pour activer la validation, vous devez faire en sorte que jQuery Validate analyse le formulaire dynamique immédiatement après l’avoir créé. Par exemple, le code suivant définit la validation côté client sur un formulaire ajouté par le biais d’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);
}
})
La méthode $.validator.unobtrusive.parse()
accepte un sélecteur jQuery comme argument. Cette méthode indique à jQuery Unobtrusive Validation d’analyser les attributs data-
des formulaires dans ce sélecteur. Les valeurs de ces attributs sont ensuite transmises au plug-in jQuery Validate.
La méthode $.validator.unobtrusive.parse()
opère sur un formulaire entier, et non sur des contrôles individuels générés de manière dynamique tels que <input>
et <select/>
. Pour réanalyser le formulaire, supprimez les données de validation qui ont été ajoutées quand le formulaire a été analysé précédemment, comme illustré dans l’exemple suivant :
$.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 Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
La validation personnalisée côté client s’effectue en générant des attributs HTML data-
qui fonctionnent avec un adaptateur jQuery Validate personnalisé. L’exemple de code d’adaptateur suivant a été écrit pour les attributs [ClassicMovie]
et [ClassicMovieWithClientValidator]
qui ont été introduits plus haut dans cet 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;
});
Pour plus d’informations sur la façon d’écrire des adaptateurs, consultez la documentation de jQuery Validate.
L’utilisation d’un adaptateur pour un champ donné est déclenchée par des attributs data-
qui :
data-val="true"
).data-val-rulename="Error message."
).data-val-rulename-param1="value"
).L’exemple suivant montre les attributs data-
pour l’attribut de l’exemple d’application ClassicMovie
:
<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="">
Comme mentionné plus haut, les Tag Helpers et les helpers HTML utilisent les informations des attributs de validation pour restituer les attributs data-
. Il existe deux options pour l’écriture de code qui entraîne la création d’attributs HTML data-
personnalisés :
Cette méthode de rendu des attributs data-
en HTML est utilisée par l’attribut ClassicMovie
dans l’exemple d’application. Pour ajouter la validation côté client à l’aide de cette méthode
Créez une classe d’adaptateurs d’attributs pour l’attribut de validation personnalisé. Dérivez la classe de AttributeAdapterBase<TAttribute>. Créez une méthode AddValidation
qui ajoute des attributs data-
à la sortie restituée, comme illustré dans cet exemple :
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();
}
Créez une classe de fournisseurs d’adaptateurs qui implémente IValidationAttributeAdapterProvider. Dans la méthode GetAttributeAdapter, passez l’attribut personnalisé au constructeur de l’adaptateur, comme illustré dans cet exemple :
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);
}
}
Inscrivez le fournisseur d’adaptateurs auprès de l’injection de dépendances dans Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Cette méthode de rendu des attributs data-
en HTML est utilisée par l’attribut ClassicMovieWithClientValidator
dans l’exemple d’application. Pour ajouter la validation côté client à l’aide de cette méthode
Dans l’attribut de validation personnalisé, implémentez l’interface IClientModelValidator et créez une méthode AddValidation. Dans la méthode AddValidation
, ajoutez des attributs data-
pour la validation, comme indiqué dans l’exemple suivant :
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 static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
Le code suivant désactive la validation côté client dans Razor Pages :
builder.Services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Autres options pour désactiver la validation côté client :
_ValidationScriptsPartial
dans tous les fichiers .cshtml
.L’approche précédente n’empêche pas la validation côté client de la bibliothèque de classes IdentityRazor ASP.NET Core. Pour plus d’informations, consultez Génération de modèles automatiqueIdentity dans les projets ASP.NET Core.
Cet article explique comment valider l’entrée utilisateur dans ASP.NET Core MVC ou l’application Razor Pages.
Affichez ou téléchargez un exemple de code (procédure de téléchargement).
L’état du modèle représente les erreurs qui proviennent de deux sous-systèmes : liaison de modèle et validation de modèle. Les erreurs qui proviennent de la liaison de modèle sont généralement des erreurs de conversion de données. Par exemple, un « x » est entré dans un champ de nombres entiers. La validation du modèle se produit après la liaison de modèle et signale des erreurs lorsque les données ne sont pas conformes aux règles d’entreprise. Par exemple, un 0 est entré dans un champ qui attend une évaluation comprise entre 1 et 5.
La liaison et la validation de modèle se produisent avant l’exécution d’une action de contrôleur ou d’une méthode de gestionnaire Razor Pages. Pour les applications web, il incombe à l’application d’inspecter ModelState.IsValid
et de réagir de façon appropriée. En règle générale, les applications web réaffichent la page avec un message d’erreur :
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Les contrôleurs d’API web ne sont pas obligés de vérifier s’ils ont l’attribut ModelState.IsValid
[ApiController]. Dans ce cas, une réponse HTTP 400 automatique contenant les détails du problème est retournée si l’état du modèle n’est pas valide. Pour plus d’informations, consultez Réponses HTTP 400 automatiques.
La validation est automatique, mais vous souhaiterez peut-être la répéter manuellement. Par exemple, vous pourriez calculer une valeur pour une propriété, et souhaiter réexécuter la validation après avoir affecté la valeur calculée comme valeur de la propriété. Pour réexécuter la validation, appelez ModelStateDictionary.ClearValidationState pour effacer la validation spécifique au modèle en cours de validation, suivi de TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Les attributs de validation vous permettent de spécifier des règles de validation pour des propriétés de modèle. L’exemple suivant tiré de l’exemple d’application montre une classe de modèle annotée avec des attributs de validation. L’attribut [ClassicMovie]
est un attribut de validation personnalisé et les autres sont prédéfinis. [ClassicMovieWithClientValidator]
n’apparaît pas, cela montre une autre façon d’implémenter un attribut personnalisé.
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; }
}
Voici certains des attributs de validation prédéfinis :
[Required]
.[Remote]
Attribut.Vous trouverez la liste complète des attributs de validation dans l’System.ComponentModel.DataAnnotations espace de noms.
Les attributs de validation vous permettent de spécifier le message d’erreur à afficher pour l’entrée non valide. Par exemple :
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
En interne, les attributs appellent String.Format avec un espace réservé pour le nom de champ et parfois d’autres espaces réservés. Par exemple :
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Appliqué à une propriété Name
, le message d’erreur créé par le code précédent serait « Name length must be between 6 and 8 ».
Pour savoir quels paramètres sont passés à String.Format
pour le message d’erreur d’un attribut particulier, consultez le code source de DataAnnotations.
Par défaut, le système de validation traite les propriétés ou paramètres n’acceptant pas les valeurs Null comme s’ils avaient un attribut [Required(AllowEmptyStrings = true)]
. En activant Nullable
les contextes, MVC commence implicitement à valider les propriétés ou paramètres non null comme s’ils avaient l’attribut [Required(AllowEmptyStrings = true)]
. Prenez le code suivant :
public class Person
{
public string Name { get; set; }
}
Si l’application a été générée avec <Nullable>enable</Nullable>
, une valeur manquante pour Name
dans une publication JSON ou un formulaire entraîne une erreur de validation. Utilisez un type de référence Null pour autoriser la spécification de valeurs Null ou manquantes pour la propriété Name
:
public class Person
{
public string? Name { get; set; }
}
Ce comportement peut être désactivé en configurant SuppressImplicitRequiredAttributeForNonNullableReferenceTypes dans Startup.ConfigureServices
:
services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Sur le serveur, une valeur obligatoire est considérée comme manquante si la propriété est Null. Un champ n’acceptant pas les valeurs Null est toujours valide, et le message d’erreur de l’attribut [Required]
n’est jamais affiché.
Toutefois, la liaison de modèle pour une propriété n’acceptant pas les valeurs Null peut échouer, entraînant l’affichage d’un message d’erreur tel que The value '' is invalid
. Pour spécifier un message d’erreur personnalisé pour la validation côté serveur des types n’acceptant pas les valeurs Null, vous disposez des options suivantes :
Rendre le champ Nullable (par exemple, decimal?
au lieu de decimal
). Les types valeur Null<T> sont traités comme des types Null standard.
Spécifier le message d’erreur par défaut devant être utilisé par la liaison de modèle, comme indiqué dans l’exemple suivant :
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
Pour plus d’informations sur les erreurs de liaison de modèle pour lesquelles vous pouvez définir des messages par défaut, consultez DefaultModelBindingMessageProvider.
Les chaînes et les types n’acceptant pas les valeurs Null sont gérés différemment sur le client et sur le serveur. Sur le client :
Comme indiqué précédemment, les types n’acceptant pas les valeurs Null sont traités comme s’ils avaient un attribut [Required(AllowEmptyStrings = true)]
. Cela signifie que vous bénéficiez d’une validation côté client même si vous n’appliquez pas l’attribut [Required(AllowEmptyStrings = true)]
. En revanche, si vous n’utilisez pas l’attribut, vous recevez un message d’erreur par défaut. Pour spécifier un message d’erreur personnalisé, utilisez l’attribut.
L’attribut [Remote] implémente la validation côté client qui nécessite l’appel d’une méthode sur le serveur pour déterminer si le champ d’entrée est valide. Par exemple, l’application peut avoir besoin de vérifier si un nom d’utilisateur est déjà en cours d’utilisation.
Pour implémenter la validation à distance
Créez une méthode d’action devant être appelée par JavaScript. La méthode de validation jQuery remote attend une réponse JSON :
true
signifie que les données d’entrée sont valides.false
, undefined
ou null
signifie que l’entrée n’est pas valide. Affichez le message d’erreur par défaut.Voici un exemple de méthode d’action qui retourne un message d’erreur personnalisé :
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
Dans la classe de modèle, annotez la propriété avec un attribut [Remote]
qui pointe vers la méthode d’action de validation, comme indiqué dans l’exemple suivant :
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; }
La propriété AdditionalFields de l’attribut [Remote]
vous permet de valider des combinaisons de champs relativement à des données présentes sur le serveur. Par exemple, si le modèle User
a des propriétés FirstName
et LastName
, vous pouvez vérifier qu’aucun utilisateur existant n’a déjà cette paire de noms. L'exemple suivant montre comment utiliser 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
peut être défini explicitement sur les chaînes «Prénom» et «Nom», mais utiliser le nom de l’opérateur simplifie la refactorisation ultérieure. La méthode d’action pour cette validation doit accepter à la fois les arguments de firstName
et lastName
:
[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);
}
Quand l’utilisateur entre un nom ou un prénom, JavaScript effectue un appel à distance pour vérifier si cette paire de noms est déjà utilisée.
Pour valider deux champs supplémentaires ou plus, spécifiez-les sous la forme d’une liste délimitée par des virgules. Par exemple, pour ajouter une propriété MiddleName
au modèle, définissez l’attribut [Remote]
comme indiqué dans l’exemple suivant :
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields
, comme tous les arguments d’attribut, doit être une expression constante. Vous ne devez donc pas utiliser une chaîne interpolée ou appeler Join pour initialiser AdditionalFields
.
Si vous avez besoin d’une validation non fournie par les attributs prédéfinis, vous pouvez :
Pour les scénarios non gérés par les attributs de validation prédéfinis, vous pouvez créer des attributs de validation personnalisés. Créez une classe qui hérite de ValidationAttribute et substituez la méthode IsValid.
La méthode IsValid
accepte un objet nommé value, qui est l’entrée à valider. Une surcharge accepte également un objet ValidationContext, qui fournit des informations supplémentaires telles que l’instance de modèle créée par la liaison de modèle.
L’exemple suivant vérifie que la date de sortie d’un film appartenant au genre Classic n’est pas ultérieure à une année spécifiée. L’attribut [ClassicMovie]
:
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;
}
}
La variable movie
de l’exemple précédent représente un objet Movie
qui contient les données de l’envoi du formulaire. Quand la validation échoue, un ValidationResult avec un message d’erreur est retourné.
L’exemple précédent fonctionne uniquement avec les types Movie
. Une autre option de validation au niveau de la classe consiste à implémenter IValidatableObject dans la classe de modèle, comme indiqué dans l’exemple suivant :
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) });
}
}
}
Les nœuds de niveau supérieur incluent les éléments suivants :
Les nœuds de niveau supérieur liés au modèle sont validés en plus de la validation des propriétés du modèle. Dans l’exemple suivant tiré de l’exemple d’application, la méthodeVerifyPhone
utilise RegularExpressionAttribute pour valider le paramètre d’action phone
:
[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);
}
Les nœuds de niveau supérieur peuvent utiliser BindRequiredAttribute avec des attributs de validation. Dans l’exemple suivant de l’exemple d’application, la méthodeCheckAge
spécifie que le paramètre age
doit être lié à partir de la chaîne de requête au moment de l’envoi du formulaire :
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
Dans la page de vérification de l’âge (CheckAge.cshtml
), il y a deux formulaires. Le premier formulaire envoie une valeur Age
égale à 99
en tant que chaîne de requête : https://localhost:5001/Users/CheckAge?Age=99
.
Quand un paramètre age
au format approprié est envoyé à partir de la chaîne de requête, le formulaire est validé.
Le second formulaire de la page de vérification de l’âge envoie la valeur Age
dans le corps de la requête, ce qui entraîne un échec de la validation. L’échec de la liaison est dû au fait que le paramètre age
doit provenir d’une chaîne de requête.
La validation s’arrête quand le nombre maximal d’erreurs est atteint (200 par défaut). Vous pouvez configurer ce nombre avec le code suivant dans Startup.ConfigureServices
:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
ValidationVisitor parcourt le graphe d’objet du modèle en cours de validation. Pour les modèles très profonds ou infiniment récursifs, la validation peut entraîner un dépassement de la capacité de la pile. MvcOptions.MaxValidationDepth fournit un moyen d’interrompre la validation de manière anticipée si la récursivité du visiteur dépasse une profondeur configurée. La valeur par défaut de MvcOptions.MaxValidationDepth
est 32.
La validation est automatiquement court-circuitée (ignorée) si le graphe du modèle ne nécessite pas de validation. Les objets pour lesquels le runtime ignore la validation comprennent les collections de primitives (telles que byte[]
, string[]
, Dictionary<string, string>
) et les graphes d’objets complexes qui n’ont pas de validateur.
La validation côté client empêche l’envoi jusqu’à ce que le formulaire soit valide. Le bouton Submit exécute le code JavaScript qui envoie le formulaire ou qui affiche des messages d’erreur.
La validation côté client permet d’éviter un aller-retour inutile vers le serveur quand il existe des erreurs d’entrée sur un formulaire. Les références de script suivantes dans _Layout.cshtml
et _ValidationScriptsPartial.cshtml
valident du côté du client:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js"></script>
Le script jQuery Unobtrusive Validation est une bibliothèque frontale personnalisée de Microsoft qui s’appuie sur le plug-in bien connu jQuery Validate. Sans jQuery Unobtrusive Validation, vous devriez coder la même logique de validation à deux endroits : une fois dans les attributs de validation côté serveur sur les propriétés du modèle, puis à nouveau dans les scripts côté client. Au lieu de cela, les Tag Helpers et les helpers HTML utilisent les attributs de validation et les métadonnées de type des propriétés du modèle afin de restituer les attributs data-
HTML 5 pour les éléments de formulaire nécessitant une validation. La validation non obstrustice jQuery analyse les attributs data-
et transmet la logique à jQuery Validate, en « copiant » la logique de validation côté serveur vers le client. Vous pouvez afficher les erreurs de validation sur le client en utilisant des Tag Helpers, comme indiqué ici :
<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>
Les assistants de balisage précédents restituent le code HTML suivant :
<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>
Notez que les attributs data-
dans la sortie HTML correspondent aux attributs de validation pour la propriété Movie.ReleaseDate
. L’attribut data-val-required
contient un message d’erreur à afficher si l’utilisateur ne renseigne pas le champ correspondant à la date de sortie. La validation non obstructive jQuery transmet cette valeur à la méthode de validation jQuery requise, qui affiche alors ce message dans l’élément <span > qui l’accompagne.
La validation du type de données est basée sur le type .NET d’une propriété, sauf en cas de substitution par un attribut [DataType] . Les navigateurs ont leurs propres messages d’erreur par défaut, mais le package jQuery Validation Unobtrusive Validation peut remplacer ces messages. Les attributs [DataType]
et les sous-classes comme [EmailAddress] permettent de spécifier le message d’erreur.
Pour plus d’informations sur la validation non obstructive, consultez ce problème GitHub.
La validation jQuery non obstructive transmet la logique et les paramètres de validation à jQuery Validate lors du premier chargement de la page. Par conséquent, la validation ne fonctionne pas automatiquement sur les formulaires générés de manière dynamique. Pour activer la validation, vous devez faire en sorte que jQuery Validate analyse le formulaire dynamique immédiatement après l’avoir créé. Par exemple, le code suivant définit la validation côté client sur un formulaire ajouté par le biais d’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);
}
})
La méthode $.validator.unobtrusive.parse()
accepte un sélecteur jQuery comme argument. Cette méthode indique à jQuery Unobtrusive Validation d’analyser les attributs data-
des formulaires dans ce sélecteur. Les valeurs de ces attributs sont ensuite transmises au plug-in jQuery Validate.
La méthode $.validator.unobtrusive.parse()
opère sur un formulaire entier, et non sur des contrôles individuels générés de manière dynamique tels que <input>
et <select/>
. Pour réanalyser le formulaire, supprimez les données de validation qui ont été ajoutées quand le formulaire a été analysé précédemment, comme illustré dans l’exemple suivant :
$.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 Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
La validation personnalisée côté client s’effectue en générant des attributs HTML data-
qui fonctionnent avec un adaptateur jQuery Validate personnalisé. L’exemple de code d’adaptateur suivant a été écrit pour les attributs [ClassicMovie]
et [ClassicMovieWithClientValidator]
qui ont été introduits plus haut dans cet 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;
});
Pour plus d’informations sur la façon d’écrire des adaptateurs, consultez la documentation de jQuery Validate.
L’utilisation d’un adaptateur pour un champ donné est déclenchée par des attributs data-
qui :
data-val="true"
).data-val-rulename="Error message."
).data-val-rulename-param1="value"
).L’exemple suivant montre les attributs data-
pour l’attribut de l’exemple d’application ClassicMovie
:
<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="">
Comme mentionné plus haut, les Tag Helpers et les helpers HTML utilisent les informations des attributs de validation pour restituer les attributs data-
. Il existe deux options pour l’écriture de code qui entraîne la création d’attributs HTML data-
personnalisés :
Cette méthode de rendu des attributs data-
en HTML est utilisée par l’attribut ClassicMovie
dans l’exemple d’application. Pour ajouter la validation côté client à l’aide de cette méthode
Créez une classe d’adaptateurs d’attributs pour l’attribut de validation personnalisé. Dérivez la classe de AttributeAdapterBase<TAttribute>. Créez une méthode AddValidation
qui ajoute des attributs data-
à la sortie restituée, comme illustré dans cet exemple :
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();
}
Créez une classe de fournisseurs d’adaptateurs qui implémente IValidationAttributeAdapterProvider. Dans la méthode GetAttributeAdapter, passez l’attribut personnalisé au constructeur de l’adaptateur, comme illustré dans cet exemple :
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);
}
}
Inscrivez le fournisseur d’adaptateurs auprès de l’injection de dépendances dans Startup.ConfigureServices
:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
Cette méthode de rendu des attributs data-
en HTML est utilisée par l’attribut ClassicMovieWithClientValidator
dans l’exemple d’application. Pour ajouter la validation côté client à l’aide de cette méthode
Dans l’attribut de validation personnalisé, implémentez l’interface IClientModelValidator et créez une méthode AddValidation. Dans la méthode AddValidation
, ajoutez des attributs data-
pour la validation, comme indiqué dans l’exemple suivant :
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;
}
}
Le code suivant désactive la validation côté client dans Razor Pages :
services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Autres options pour désactiver la validation côté client :
_ValidationScriptsPartial
dans tous les fichiers .cshtml
.L’approche précédente n’empêche pas la validation côté client de la bibliothèque de classes IdentityRazor ASP.NET Core. Pour plus d’informations, consultez Génération de modèles automatiqueIdentity dans les projets ASP.NET Core.
Commentaires sur ASP.NET Core
ASP.NET Core est un projet open source. Sélectionnez un lien pour fournir des commentaires :
Événement
Championnats du monde Power BI DataViz
14 févr., 16 h - 31 mars, 16 h
Avec 4 chances d’entrer, vous pourriez gagner un package de conférence et le rendre à la Live Grand Finale à Las Vegas
En savoir plus