Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Note
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 10 de cet article.
Warning
Cette version d'ASP.NET Core n'est plus prise en charge. Pour plus d’informations, consultez la stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 10 de cet article.
Cet article explique comment utiliser la validation dans les formulaires Blazor.
Validation de formulaires
Dans les scénarios de validation de formulaire de base, une instance EditForm peut utiliser des instances EditContext et ValidationMessageStore déclarées pour valider les champs du formulaire. Un gestionnaire pour l’événement OnValidationRequested de EditContext exécute une logique de validation personnalisée. Le résultat du gestionnaire met à jour l’instance ValidationMessageStore.
La validation de base des formulaires est utile lorsque le modèle du formulaire est défini dans le composant qui héberge le formulaire, soit en tant que membres directement sur le composant, soit dans une sous-classe. L’utilisation d’un composant de validation est recommandée lorsqu’une classe de modèle indépendante est utilisée dans plusieurs composants.
Dans les Blazor Web Apps, la validation côté client nécessite un circuit BlazorSignalR actif. La validation côté client n’est pas disponible pour les formulaires dans les composants qui ont adopté le rendu statique côté serveur (SSR statique). Les formulaires qui adoptent le SSR statique sont validés sur le serveur après la soumission du formulaire.
Dans le composant suivant, la méthode de gestionnaire HandleValidationRequested efface tous les messages de validation existants en appelant ValidationMessageStore.Clear avant de valider le formulaire.
Starship8.razor :
@page "/starship-8"
@implements IDisposable
@inject ILogger<Starship8> Logger
<h2>Holodeck Configuration</h2>
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship8">
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem1" />
Safety Subsystem
</label>
</div>
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem2" />
Emergency Shutdown Subsystem
</label>
</div>
<div>
<ValidationMessage For="() => Model!.Options" />
</div>
<div>
<button type="submit">Update</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Holodeck? Model { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// Custom validation logic
if (!Model!.Options)
{
messageStore?.Add(() => Model.Options, "Select at least one.");
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Holodeck
{
public bool Subsystem1 { get; set; }
public bool Subsystem2 { get; set; }
public bool Options => Subsystem1 || Subsystem2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
@page "/starship-8"
@implements IDisposable
@inject ILogger<Starship8> Logger
<h2>Holodeck Configuration</h2>
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship8">
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem1" />
Safety Subsystem
</label>
</div>
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem2" />
Emergency Shutdown Subsystem
</label>
</div>
<div>
<ValidationMessage For="() => Model!.Options" />
</div>
<div>
<button type="submit">Update</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Holodeck? Model { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// Custom validation logic
if (!Model!.Options)
{
messageStore?.Add(() => Model.Options, "Select at least one.");
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Holodeck
{
public bool Subsystem1 { get; set; }
public bool Subsystem2 { get; set; }
public bool Options => Subsystem1 || Subsystem2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
@page "/starship-8"
@implements IDisposable
@inject ILogger<Starship8> Logger
<h2>Holodeck Configuration</h2>
<EditForm EditContext="editContext" OnValidSubmit="Submit">
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem1" />
Safety Subsystem
</label>
</div>
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem2" />
Emergency Shutdown Subsystem
</label>
</div>
<div>
<ValidationMessage For="() => Model!.Options" />
</div>
<div>
<button type="submit">Update</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
public Holodeck? Model { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// Custom validation logic
if (!Model!.Options)
{
messageStore?.Add(() => Model.Options, "Select at least one.");
}
}
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public class Holodeck
{
public bool Subsystem1 { get; set; }
public bool Subsystem2 { get; set; }
public bool Options => Subsystem1 || Subsystem2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
Annotations de données Composant Validator et validation personnalisée
Le composant DataAnnotationsValidator associe la validation des annotations de données à un EditContext en cascade. Pour activer la validation des annotations de données, le composant DataAnnotationsValidator est requis. Pour utiliser un système de validation différent des annotations de données, utilisez une implémentation personnalisée à la place du composant DataAnnotationsValidator. Les implémentations du framework pour DataAnnotationsValidator sont disponibles pour inspection dans la source de référence :
Pour plus d’informations sur le comportement de validation, consultez la DataAnnotationsValidator section comportement de validation .
Si vous devez activer la prise en charge de la validation des annotations de données pour un EditContext dans le code, appelez EnableDataAnnotationsValidation avec un IServiceProvider injecté (@inject IServiceProvider ServiceProvider) sur le EditContext. Pour obtenir un exemple avancé, consultez le NotifyPropertyChangedValidationComponent composant du framework Blazor ASP.NET Core BasicTestApp (dotnet/aspnetcoredépôt GitHub). Dans une version de production de l’exemple, remplacez l’argument new TestServiceProvider() du fournisseur de services par un injecté IServiceProvider.
Note
Les liens de documentation vers la source de référence .NET chargent généralement la branche par défaut du référentiel, qui représente le développement actuel pour la prochaine version de .NET. Pour sélectionner une balise pour une version spécifique, utilisez la liste déroulante Échanger les branches ou les balises. Pour plus d’informations, consultez Comment sélectionner une balise de version du code source ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Blazoreffectue deux types de validation :
- La validation des champs est effectuée lorsque l’utilisateur quitte un champ. Pendant la validation du champ, le composant DataAnnotationsValidator associe tous les résultats de validation signalés au champ.
- La validation du modèle est effectuée lorsque l’utilisateur soumet le formulaire. Pendant la validation du modèle, le composant DataAnnotationsValidator tente de déterminer le champ en fonction du nom du membre signalé par le résultat de la validation. Les résultats de validation qui ne sont pas associés à un membre individuel sont associés au modèle plutôt qu’à un champ.
Dans les scénarios de validation personnalisés :
- La validation gère un ValidationMessageStore pour le EditContextd’un formulaire.
- Le DataAnnotationsValidator composant est utilisé pour attacher la prise en charge de la validation aux formulaires en fonction des attributs de validation (annotations de données) .
Il existe deux approches générales pour la validation personnalisée, qui sont décrites dans les deux sections suivantes de cet article :
-
Validation manuelle à l’aide de l’événement
OnValidationRequested: validez manuellement les champs d’un formulaire avec la validation des annotations de données et le code personnalisé pour les vérifications de champ lorsque la validation est demandée via un gestionnaire d’événements affecté à l’événementOnValidationRequested. - Composants du validateur : un ou plusieurs composants de validateur personnalisés peuvent être utilisés pour traiter la validation pour différents formulaires sur la même page ou le même formulaire à différentes étapes de traitement de formulaire (par exemple, la validation du client suivie de la validation du serveur).
Validation manuelle à l’aide de l’événement OnValidationRequested
Vous pouvez valider manuellement un formulaire avec un gestionnaire d’événements personnalisé affecté à l’événement EditContext.OnValidationRequested pour gérer un ValidationMessageStore.
L’infrastructure Blazor fournit le composant pour attacher une DataAnnotationsValidator prise en charge de validation supplémentaire aux formulaires en fonction des attributs de validation (annotations de données).
Rappelant l’exemple de composant précédent Starship8 , la HandleValidationRequested méthode est affectée à OnValidationRequested, où vous pouvez effectuer une validation manuelle dans le code C#. Quelques modifications illustrent la combinaison de la validation manuelle existante avec la validation des annotations de données via un DataAnnotationsValidator et un attribut de validation appliqué au modèle Holodeck.
Référez-vous à l'espace de noms System.ComponentModel.DataAnnotations dans les directives de Razor situées en haut du fichier de définition du composant.
@using System.ComponentModel.DataAnnotations
Ajoutez une Id propriété au Holodeck modèle avec un attribut de validation pour limiter la longueur de la chaîne à six caractères :
[StringLength(6)]
public string? Id { get; set; }
Ajoutez un DataAnnotationsValidator composant (<DataAnnotationsValidator />) au formulaire. En règle générale, le composant est placé immédiatement sous la <EditForm> balise, mais vous pouvez le placer n’importe où dans le formulaire :
<DataAnnotationsValidator />
Modifiez le comportement d'envoi du formulaire dans la balise <EditForm> de OnSubmit à OnValidSubmit, ce qui garantit que le formulaire est valide avant d'exécuter la méthode de gestionnaire d'événements assignée :
- OnSubmit="Submit"
+ OnValidSubmit="Submit"
Dans le <EditForm>, ajoutez un champ pour la Id propriété :
<div>
<label>
<InputText @bind-Value="Model!.Id" />
ID (6 characters max)
</label>
<ValidationMessage For="() => Model!.Id" />
</div>
Après avoir apporté les modifications précédentes, le comportement du formulaire correspond à la spécification suivante :
- La validation des annotations de données sur la
Idpropriété ne déclenche pas d’échec de validation lorsque le champ perd simplement leIdfocus. La validation s’exécute lorsque l’utilisateur sélectionne leUpdatebouton. - Toute validation manuelle que vous souhaitez effectuer dans la
HandleValidationRequestedméthode affectée à l’événement du OnValidationRequested formulaire s’exécute lorsque l’utilisateur sélectionne le bouton duUpdateformulaire. Dans le code existant de l’exempleStarship8de composant, l’utilisateur doit sélectionner l’une ou l’autre des cases à cocher pour valider le formulaire. - Le formulaire ne traite pas la
Submitméthode tant que les annotations de données et la validation manuelle ne sont pas passées.
Composants de validation
Les composants de validation prennent en charge la validation des formulaires en gérant un ValidationMessageStore pour le EditContext d’un formulaire.
Le framework Blazor fournit le composant DataAnnotationsValidator pour associer la prise en charge de la validation aux formulaires en fonction des attributs de validation (annotations de données). Vous pouvez créer des composants de validateur personnalisés pour traiter les messages de validation pour différents formulaires sur la même page ou le même formulaire à différentes étapes du traitement du formulaire (par exemple, validation client suivie d’une validation serveur). L’exemple de composant de validateur présenté dans cette section, CustomValidation, est utilisé dans les sections suivantes de cet article :
- Validation de la logique métier avec un composant de validateur
- Validation serveur avec un composant validateur
Parmi les validateurs intégrés d’annotation de données, seul l’attribut de validation [Remote] n’est pas pris en charge dans Blazor.
Note
Dans de nombreux cas, des attributs de validation d’annotation de données personnalisés peuvent être utilisés à la place des composants validateurs personnalisés. Les attributs personnalisés appliqués au modèle du formulaire sont activés à l’aide du composant DataAnnotationsValidator Lorsqu’ils sont utilisés avec la validation serveur, tous les attributs personnalisés appliqués au modèle doivent être exécutables sur le serveur. Pour plus d’informations, consultez la section Attributs de validation personnalisés.
Créer un composant validateur à partir de ComponentBase :
- Le EditContext du formulaire est un paramètre en cascade du composant.
- Lorsque le composant validateur est initialisé, un nouveau ValidationMessageStore est créé pour conserver une liste à jour des erreurs du formulaire.
- Le magasin de messages reçoit les erreurs lorsque le code développeur du composant du formulaire appelle la méthode
DisplayErrors. Les erreurs sont transmises à la méthodeDisplayErrorsdans unDictionary<string, List<string>>. Dans le dictionnaire, la clé est le nom du champ du formulaire qui contient une ou plusieurs erreurs. La valeur est la liste des erreurs. - Les messages sont effacés lorsque l’une des situations suivantes se produit :
- La validation est demandée sur le EditContext lorsque l’événement OnValidationRequested est déclenché. Toutes les erreurs sont effacées.
- Un champ change dans le formulaire lorsque l’événement OnFieldChanged est déclenché. Seules les erreurs du champ sont effacées.
- La méthode
ClearErrorsest appelée par le code du développeur. Toutes les erreurs sont effacées.
Mettez à jour l’espace de noms dans la classe suivante afin qu’il corresponde à l’espace de noms de votre application.
CustomValidation.cs :
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
namespace BlazorSample;
public class CustomValidation : ComponentBase
{
private ValidationMessageStore? messageStore;
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
protected override void OnInitialized()
{
if (CurrentEditContext is null)
{
throw new InvalidOperationException(
$"{nameof(CustomValidation)} requires a cascading " +
$"parameter of type {nameof(EditContext)}. " +
$"For example, you can use {nameof(CustomValidation)} " +
$"inside an {nameof(EditForm)}.");
}
messageStore = new(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) =>
messageStore?.Clear();
CurrentEditContext.OnFieldChanged += (s, e) =>
messageStore?.Clear(e.FieldIdentifier);
}
public void DisplayErrors(Dictionary<string, List<string>> errors)
{
if (CurrentEditContext is not null)
{
foreach (var err in errors)
{
messageStore?.Add(CurrentEditContext.Field(err.Key), err.Value);
}
CurrentEditContext.NotifyValidationStateChanged();
}
}
public void ClearErrors()
{
messageStore?.Clear();
CurrentEditContext?.NotifyValidationStateChanged();
}
}
Importante
La spécification d’un espace de noms est requise lors de la dérivation à partir de ComponentBase. Si vous ne spécifiez pas d’espace de noms, une erreur de compilation se produit :
Tag helpers cannot target tag name '<global namespace>.{CLASS NAME}' because it contains a ' ' character.
L’espace réservé {CLASS NAME} correspond au nom de la classe de composant. L’exemple de validateur personnalisé de cette section spécifie l’espace de noms BlazorSample.
Note
Les expressions lambda anonymes sont des gestionnaires d’événements enregistrés pour OnValidationRequested et OnFieldChanged dans l’exemple précédent. Il n’est pas nécessaire d’implémenter IDisposable et de désabonner les délégués d’événement dans ce scénario. Pour plus d’informations, consultez Suppression de composants ASP.NET Core Razor.
Validation de la logique métier avec un composant validateur
Pour la validation générale de la logique métier, utilisez un composant validateur qui reçoit les erreurs de formulaire dans un dictionnaire.
La validation de base est utile lorsque le modèle du formulaire est défini dans le composant qui héberge le formulaire, soit en tant que membres directement sur le composant, soit dans une sous-classe. L’utilisation d’un composant validateur est recommandée lorsqu’une classe de modèle indépendante est utilisée dans plusieurs composants.
Dans l’exemple suivant :
- Une version raccourcie du formulaire
Starfleet Starship Database(composantStarship3) de la section Formulaire d’exemple de l’article Composants d’entrée est utilisée, qui n’accepte que la classification et la description du vaisseau spatial. La validation des annotations de données n’est pas déclenchée lors de la soumission du formulaire, car le composant DataAnnotationsValidator n’est pas inclus dans le formulaire. - Le composant
CustomValidationde la section Composants validateurs de cet article est utilisé. - La validation nécessite une valeur pour la description du vaisseau (
Description) si l’utilisateur sélectionne la classification de vaisseau «Defense» (Classification).
Lorsque des messages de validation sont définis dans le composant, ils sont ajoutés au ValidationMessageStore du validateur et affichés dans le résumé de validation du EditForm.
Starship9.razor :
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">
Select classification ...
</option>
<option checked="@(Model!.Classification == "Exploration")"
value="Exploration">
Exploration
</option>
<option checked="@(Model!.Classification == "Diplomacy")"
value="Diplomacy">
Diplomacy
</option>
<option checked="@(Model!.Classification == "Defense")"
value="Defense">
Defense
</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
[ "For a 'Defense' ship classification, " +
"'Description' is required." ]);
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">
Select classification ...
</option>
<option checked="@(Model!.Classification == "Exploration")"
value="Exploration">
Exploration
</option>
<option checked="@(Model!.Classification == "Diplomacy")"
value="Diplomacy">
Diplomacy
</option>
<option checked="@(Model!.Classification == "Defense")"
value="Defense">
Defense
</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
[ "For a 'Defense' ship classification, " +
"'Description' is required." ]);
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
public Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
new() { "For a 'Defense' ship classification, " +
"'Description' is required." });
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
Note
Au lieu d’utiliser des composants de validation, vous pouvez utiliser des attributs de validation d’annotation de données. Les attributs personnalisés appliqués au modèle du formulaire sont activés à l’aide du composant DataAnnotationsValidator Lorsqu’ils sont utilisés avec la validation serveur, les attributs doivent être exécutables sur le serveur. Pour plus d’informations, consultez la section Attributs de validation personnalisés.
Validation serveur avec un composant validateur
Cette section se concentre sur les scénarios Blazor Web App, mais l’approche pour tout type d’application qui utilise la validation serveur avec l’API Web adopte la même approche générale.
Cette section se concentre sur les scénarios Blazor WebAssembly hébergés, mais l’approche pour tout type d’application qui utilise la validation serveur avec l’API Web adopte la même approche générale.
La validation serveur est prise en charge en plus de la validation client :
- Traitez la validation client dans le formulaire avec le composant DataAnnotationsValidator.
- Lorsque le formulaire passe la validation client (OnValidSubmit est appelé), envoyez le EditContext.Model à une API back-end pour le traitement du formulaire.
- Traitez la validation du modèle sur le serveur.
- L’API serveur inclut à la fois la validation des annotations de données du framework intégré et la logique de validation personnalisée fournie par le développeur. Si la validation passe sur le serveur, traitez le formulaire et renvoyez un code d’état de réussite (
200 - OK). Si la validation échoue, renvoyez un code d’état d’échec (400 - Bad Request) et les erreurs de validation du champ. - Désactivez le formulaire en cas de succès ou affichez les erreurs.
La validation de base est utile lorsque le modèle du formulaire est défini dans le composant qui héberge le formulaire, soit en tant que membres directement sur le composant, soit dans une sous-classe. L’utilisation d’un composant validateur est recommandée lorsqu’une classe de modèle indépendante est utilisée dans plusieurs composants.
L’exemple suivant est basé sur :
- Un Blazor Web App composants WebAssembly interactifs créés à partir du modèle de projet Blazor Web App.
- Le modèle
Starship(Starship.cs) de la section Formulaire d’exemple de l’article Composants d’entrée. - Le composant
CustomValidationprésenté dans la section Composants de validation.
Placez le modèle Starship (Starship.cs) dans un projet de bibliothèque de classes partagée afin que les projets client et serveur puissent utiliser le modèle. Ajoutez ou mettez à jour l’espace de noms pour qu’il corresponde à celui de l’application partagée (par exemple, namespace BlazorSample.Shared). Comme le modèle nécessite des annotations de données, vérifiez que la bibliothèque de classes partagée utilise le framework partagé ou ajoutez le package System.ComponentModel.Annotations au projet partagé.
Note
Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.
Dans le projet principal de Blazor Web App, ajoutez un contrôleur pour traiter les requêtes de validation des vaisseaux spatiaux et renvoyer les messages de validation échoués. Mettez à jour les espaces de noms dans la dernière instruction using pour le projet de bibliothèque de classes partagée et namespace pour la classe de contrôleur. Outre la validation des annotations de données client et serveur, le contrôleur vérifie qu’une valeur est fournie pour la description du vaisseau (Description) si l’utilisateur sélectionne la classification de vaisseau Defense (Classification).
- Une Blazor WebAssemblysolution hébergée créée à partir du modèle de projet Blazor WebAssembly. Cette approche est prise en charge pour toutes les solutions Blazor hébergées sécurisées décrites dans la documentation relative à la sécurité hébergéeBlazor WebAssembly.
- Le modèle
Starship(Starship.cs) de la section Formulaire d’exemple de l’article Composants d’entrée. - Le composant
CustomValidationprésenté dans la section Composants de validation.
Placez le modèle Starship (Starship.cs) dans le projet Shared de la solution afin que les applications client et serveur puissent utiliser le modèle. Ajoutez ou mettez à jour l’espace de noms pour qu’il corresponde à celui de l’application partagée (par exemple, namespace BlazorSample.Shared). Comme le modèle nécessite des annotations de données, ajoutez le package System.ComponentModel.Annotations au projet Shared.
Note
Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.
Dans le projet Server, ajoutez un contrôleur pour traiter les requêtes de validation des vaisseaux spatiaux et renvoyer les messages de validation échoués. Mettez à jour les espaces de noms dans la dernière instruction using pour le projet Shared et le namespace pour la classe de contrôleur. Outre la validation des annotations de données client et serveur, le contrôleur vérifie qu’une valeur est fournie pour la description du vaisseau (Description) si l’utilisateur sélectionne la classification de vaisseau Defense (Classification).
La validation de la classification des vaisseaux Defense s’effectue uniquement sur le serveur dans le contrôleur, car le formulaire suivant n’effectue pas la même validation côté client lorsque le formulaire est envoyé au serveur. La validation serveur sans validation client est courante dans les applications qui nécessitent une validation de la logique métier privée des entrées utilisateur sur le serveur. Par exemple, des informations privées provenant des données stockées pour un utilisateur peuvent être nécessaires pour valider l’entrée utilisateur. Les données privées ne peuvent évidemment pas être envoyées au client pour validation client.
Note
Le contrôleur StarshipValidation de cette section utilise Microsoft Identity 2.0. L’API Web accepte uniquement les jetons pour les utilisateurs qui ont l’étendue « API.Access » pour cette API. Une personnalisation supplémentaire est nécessaire si le nom de l’étendue de l’API est différent de API.Access.
Pour plus d’informations sur la sécurité, consultez :
- Authentification et autorisation ASP.NET Core Blazor (et les autres articles du nœud BlazorSécurité et Identity)
- Documentation sur la plateforme d'identités Microsoft
Controllers/StarshipValidation.cs :
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using BlazorSample.Shared;
namespace BlazorSample.Server.Controllers;
[Authorize]
[ApiController]
[Route("[controller]")]
public class StarshipValidationController(
ILogger<StarshipValidationController> logger)
: ControllerBase
{
static readonly string[] scopeRequiredByApi = [ "API.Access" ];
[HttpPost]
public async Task<IActionResult> Post(Starship model)
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
try
{
if (model.Classification == "Defense" &&
string.IsNullOrEmpty(model.Description))
{
ModelState.AddModelError(nameof(model.Description),
"For a 'Defense' ship " +
"classification, 'Description' is required.");
}
else
{
logger.LogInformation("Processing the form asynchronously");
// async ...
return Ok(ModelState);
}
}
catch (Exception ex)
{
logger.LogError("Validation Error: {Message}", ex.Message);
}
return BadRequest(ModelState);
}
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using BlazorSample.Shared;
namespace BlazorSample.Server.Controllers;
[Authorize]
[ApiController]
[Route("[controller]")]
public class StarshipValidationController(
ILogger<StarshipValidationController> logger)
: ControllerBase
{
static readonly string[] scopeRequiredByApi = new[] { "API.Access" };
[HttpPost]
public async Task<IActionResult> Post(Starship model)
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
try
{
if (model.Classification == "Defense" &&
string.IsNullOrEmpty(model.Description))
{
ModelState.AddModelError(nameof(model.Description),
"For a 'Defense' ship " +
"classification, 'Description' is required.");
}
else
{
logger.LogInformation("Processing the form asynchronously");
// async ...
return Ok(ModelState);
}
}
catch (Exception ex)
{
logger.LogError("Validation Error: {Message}", ex.Message);
}
return BadRequest(ModelState);
}
}
Vérifiez ou mettez à jour l’espace de noms du contrôleur précédent (BlazorSample.Server.Controllers) afin qu’il corresponde à l’espace de noms des contrôleurs de l’application.
Lorsqu’une erreur de validation de liaison de modèle se produit sur le serveur, un ApiController (ApiControllerAttribute) renvoie normalement une réponse de requête incorrecte par défaut avec un ValidationProblemDetails. La réponse contient plus de données que les erreurs de validation, comme le montre l’exemple suivant lorsque tous les champs du formulaire Starfleet Starship Database ne sont pas soumis et que le formulaire échoue à la validation :
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Id": [ "The Id field is required." ],
"Classification": [ "The Classification field is required." ],
"IsValidatedDesign": [ "This form disallows unapproved ships." ],
"MaximumAccommodation": [ "Accommodation invalid (1-100000)." ]
}
}
Note
Pour illustrer la réponse JSON précédente, vous devez soit désactiver la validation client du formulaire afin d’autoriser la soumission de champs vides, soit utiliser un outil pour envoyer une requête directement à l’API du serveur, tel que Firefox Browser Developer.
Si l’API du serveur renvoie la réponse JSON par défaut précédente, le client peut analyser la réponse dans le code développeur afin d’obtenir les enfants du nœud errors pour le traitement des erreurs de validation des formulaires. Il est peu pratique d’écrire du code de développement pour analyser le fichier. L'analyse manuelle du JSON nécessite de produire un Dictionary<string, List<string>> d'erreurs après l'appel de ReadFromJsonAsync. Idéalement, l’API du serveur ne devrait renvoyer que les erreurs de validation, comme le montre l’exemple suivant :
{
"Id": [ "The Id field is required." ],
"Classification": [ "The Classification field is required." ],
"IsValidatedDesign": [ "This form disallows unapproved ships." ],
"MaximumAccommodation": [ "Accommodation invalid (1-100000)." ]
}
Pour modifier la réponse de l’API du serveur afin qu’elle ne renvoie que les erreurs de validation, modifiez le délégué invoqué sur les actions annotées avec ApiControllerAttribute dans le fichier Program. Pour le point de terminaison d’API (/StarshipValidation), renvoyez un BadRequestObjectResult avec le ModelStateDictionary. Pour tous les autres points de terminaison d’API, conservez le comportement par défaut en renvoyant le résultat de l’objet avec un nouveau ValidationProblemDetails.
Ajoutez l’espace de noms Microsoft.AspNetCore.Mvc en haut du fichier Program dans le projet principal de Blazor Web App :
using Microsoft.AspNetCore.Mvc;
Dans le fichier Program, ajoutez ou mettez à jour la méthode d’extension AddControllersWithViews suivante et ajoutez l’appel suivant à ConfigureApiBehaviorOptions :
builder.Services.AddControllersWithViews()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
if (context.HttpContext.Request.Path == "/StarshipValidation")
{
return new BadRequestObjectResult(context.ModelState);
}
else
{
return new BadRequestObjectResult(
new ValidationProblemDetails(context.ModelState));
}
};
});
Si vous ajoutez des contrôleurs au projet principal de la Blazor Web App pour la première fois, mappez les points de terminaison du contrôleur lorsque vous placez le code précédent qui inscrit des services pour les contrôleurs. L’exemple suivant utilise les routes de contrôleur par défaut :
app.MapDefaultControllerRoute();
Note
L’exemple ci-dessus inscrit explicitement les services de contrôleur en appelant AddControllersWithViews pour atténuer automatiquement les attaques XSRF/CSRF (Cross-Site Request Forgery). Si vous utilisez simplement AddControllers, l’antifalsification n’est pas activée automatiquement.
Pour plus d’informations sur le routage des contrôleurs et les réponses d’erreur d’échec de validation, consultez les ressources suivantes :
- Routage vers des actions de contrôleur dans ASP.NET Core
- Gérer les erreurs dans les API ASP.NET Core
Dans le projet .Client, ajoutez le composant CustomValidation indiqué dans la section Composants Validator. Mettez à jour l’espace de noms pour qu’il corresponde à l’application (par exemple, namespace BlazorSample.Client).
Dans le projet .Client, le formulaire Starfleet Starship Database est mis à jour pour afficher les erreurs de validation du serveur à l’aide du composant CustomValidation. Lorsque l’API serveur renvoie des messages de validation, ceux-ci sont ajoutés à CustomValidation du composant ValidationMessageStore. Les erreurs sont disponibles dans EditContext du formulaire pour être affichées par le résumé de validation du formulaire.
Dans le composant suivant, mettez à jour l’espace de noms du projet partagé (@using BlazorSample.Shared) pour qu’il corresponde à l’espace de noms du projet partagé. Notez que le formulaire nécessite une autorisation, l’utilisateur doit donc être connecté à l’application pour accéder au formulaire.
Ajoutez l’espace de noms Microsoft.AspNetCore.Mvc en haut du fichier Program dans l’application Server :
using Microsoft.AspNetCore.Mvc;
Dans le fichier Program, recherchez la méthode d’extension AddControllersWithViews et ajoutez l’appel suivant à ConfigureApiBehaviorOptions :
builder.Services.AddControllersWithViews()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
if (context.HttpContext.Request.Path == "/StarshipValidation")
{
return new BadRequestObjectResult(context.ModelState);
}
else
{
return new BadRequestObjectResult(
new ValidationProblemDetails(context.ModelState));
}
};
});
Note
L’exemple ci-dessus inscrit explicitement les services de contrôleur en appelant AddControllersWithViews pour atténuer automatiquement les attaques XSRF/CSRF (Cross-Site Request Forgery). Si vous utilisez simplement AddControllers, l’antifalsification n’est pas activée automatiquement.
Dans le projet Client, ajoutez le composant CustomValidation indiqué dans la section Composants Validator. Mettez à jour l’espace de noms pour qu’il corresponde à l’application (par exemple, namespace BlazorSample.Client).
Dans le projet Client, le formulaire Starfleet Starship Database est mis à jour pour afficher les erreurs de validation du serveur à l’aide du composant CustomValidation. Lorsque l’API serveur renvoie des messages de validation, ceux-ci sont ajoutés à CustomValidation du composant ValidationMessageStore. Les erreurs sont disponibles dans EditContext du formulaire pour être affichées par le résumé de validation du formulaire.
Dans le composant suivant, mettez à jour l’espace de noms du projet Shared (@using BlazorSample.Shared) pour qu’il corresponde à l’espace de noms du projet partagé. Notez que le formulaire nécessite une autorisation, l’utilisateur doit donc être connecté à l’application pour accéder au formulaire.
Starship10.razor :
Note
Les formulaires basés sur EditForm activent automatiquement la prise en charge de la protection contre la falsification. Le contrôleur doit utiliser AddControllersWithViews pour enregistrer les services du contrôleur et activer automatiquement la prise en charge de la protection contre la falsification pour l’API Web.
@page "/starship-10"
@rendermode InteractiveWebAssembly
@using System.Net
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using BlazorSample.Shared
@attribute [Authorize]
@inject HttpClient Http
@inject ILogger<Starship10> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm FormName="Starship10" Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" disabled="@disabled" />
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification" disabled="@disabled">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Maximum Accommodation:
<InputNumber @bind-Value="Model!.MaximumAccommodation"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Engineering Approval:
<InputCheckbox @bind-Value="Model!.IsValidatedDesign"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Production Date:
<InputDate @bind-Value="Model!.ProductionDate" disabled="@disabled" />
</label>
</div>
<div>
<button type="submit" disabled="@disabled">Submit</button>
</div>
<div style="@messageStyles">
@message
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
private bool disabled;
private string? message;
private string messageStyles = "visibility:hidden";
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private async Task Submit(EditContext editContext)
{
customValidation?.ClearErrors();
try
{
using var response = await Http.PostAsJsonAsync<Starship>(
"StarshipValidation", (Starship)editContext.Model);
var errors = await response.Content
.ReadFromJsonAsync<Dictionary<string, List<string>>>() ??
new Dictionary<string, List<string>>();
if (response.StatusCode == HttpStatusCode.BadRequest &&
errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Validation failed. Status Code: {response.StatusCode}");
}
else
{
disabled = true;
messageStyles = "color:green";
message = "The form has been processed.";
}
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
catch (Exception ex)
{
Logger.LogError("Form processing error: {Message}", ex.Message);
disabled = true;
messageStyles = "color:red";
message = "There was an error processing the form.";
}
}
}
Le projet .Client d'un Blazor Web App doit également enregistrer un HttpClient pour les requêtes HTTP POST vers un contrôleur d'API Web de backend. Vérifiez ou ajoutez les éléments suivants au fichier .Client du projet Program :
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
L’exemple précédent définit l’adresse de base avec builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), qui obtient l’adresse de base de l’application et est généralement dérivée de la valeur <base> de la balise href dans la page hôte.
@page "/starship-10"
@using System.Net
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using BlazorSample.Shared
@attribute [Authorize]
@inject HttpClient Http
@inject ILogger<Starship10> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" disabled="@disabled" />
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification" disabled="@disabled">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Maximum Accommodation:
<InputNumber @bind-Value="Model!.MaximumAccommodation"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Engineering Approval:
<InputCheckbox @bind-Value="Model!.IsValidatedDesign"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Production Date:
<InputDate @bind-Value="Model!.ProductionDate" disabled="@disabled" />
</label>
</div>
<div>
<button type="submit" disabled="@disabled">Submit</button>
</div>
<div style="@messageStyles">
@message
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
private bool disabled;
private string? message;
private string messageStyles = "visibility:hidden";
public Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private async Task Submit(EditContext editContext)
{
customValidation?.ClearErrors();
try
{
using var response = await Http.PostAsJsonAsync<Starship>(
"StarshipValidation", (Starship)editContext.Model);
var errors = await response.Content
.ReadFromJsonAsync<Dictionary<string, List<string>>>() ??
new Dictionary<string, List<string>>();
if (response.StatusCode == HttpStatusCode.BadRequest &&
errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Validation failed. Status Code: {response.StatusCode}");
}
else
{
disabled = true;
messageStyles = "color:green";
message = "The form has been processed.";
}
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
catch (Exception ex)
{
Logger.LogError("Form processing error: {Message}", ex.Message);
disabled = true;
messageStyles = "color:red";
message = "There was an error processing the form.";
}
}
}
Note
Au lieu d’utiliser un composant de validation, vous pouvez utiliser des attributs de validation d’annotation de données. Les attributs personnalisés appliqués au modèle du formulaire sont activés à l’aide du composant DataAnnotationsValidator Lorsqu’ils sont utilisés avec la validation serveur, les attributs doivent être exécutables sur le serveur. Pour plus d’informations, consultez la section Attributs de validation personnalisés.
Note
L’approche de validation serveur décrite dans cette section convient à tous les exemples de solutions Blazor WebAssembly hébergées dans cette documentation :
InputText basé sur l’événement d’entrée
Utilisez le composant InputText pour créer un composant personnalisé qui utilise l’événement oninput (input) au lieu de l’événement onchange (change). L’utilisation de l’événement input déclenche la validation du champ à chaque frappe.
Le composant CustomInputText suivant hérite du composant InputText du framework et définit la liaison d’événement sur l’événement oninput (input).
CustomInputText.razor :
@inherits InputText
<input @attributes="AdditionalAttributes"
class="@CssClass"
@bind="CurrentValueAsString"
@bind:event="oninput" />
Le composant CustomInputText peut être utilisé partout où InputText est utilisé. Le composant suivant utilise le composant CustomInputText partagé.
Starship11.razor :
@page "/starship-11"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship11> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship11">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<CustomInputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<div>
CurrentValue: @Model?.Id
</div>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-11"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship11> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship11">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<CustomInputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<div>
CurrentValue: @Model?.Id
</div>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-11"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship11> Logger
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<CustomInputText @bind-Value="Model!.Id" />
<button type="submit">Submit</button>
</EditForm>
<div>
CurrentValue: @Model?.Id
</div>
@code {
public Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
Composants Validation Summary et Validation Message
Le composant ValidationSummary résume tous les messages de validation, de manière similaire à l’aide de balise Validation Summary :
<ValidationSummary />
Affichez les messages de validation pour un modèle spécifique à l’aide du paramètre Model :
<ValidationSummary Model="Model" />
Le composant ValidationMessage<TValue> affiche les messages de validation pour un champ spécifique, de manière similaire à l’aide de balise Validation Message. Spécifiez le champ à valider à l’aide de l’attribut For et d’une expression lambda nommant la propriété du modèle :
<ValidationMessage For="@(() => Model!.MaximumAccommodation)" />
Les composants ValidationMessage<TValue> et ValidationSummary prennent en charge des attributs arbitraires. Tout attribut qui ne correspond pas à un paramètre de composant est ajouté à l’élément <div> ou <ul> généré.
Contrôlez le style des messages de validation dans la feuille de style de l’application (wwwroot/css/app.css ou wwwroot/css/site.css). La classe validation-message par défaut définit la couleur du texte des messages de validation sur rouge :
.validation-message {
color: red;
}
Déterminer si un champ de formulaire est valide
Utilisez EditContext.IsValid pour déterminer si un champ est valide sans obtenir de messages de validation.
Pris en charge, mais non recommandé :
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
Recommandé :
var isValid = editContext.IsValid(fieldIdentifier);
Attributs de validation personnalisés
Pour vous assurer qu’un résultat de validation est correctement associé à un champ lorsque vous utilisez un attribut de validation personnalisé, transmettez le contexte de validation MemberName lors de la création du ValidationResult.
CustomValidator.cs :
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object? value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
[ validationContext.MemberName! ]);
}
}
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object? value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
new[] { validationContext.MemberName! });
}
}
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
new[] { validationContext.MemberName });
}
}
Injectez des services dans les attributs de validation personnalisés via ValidationContext. L'exemple suivant illustre un formulaire permettant de composer une salade, tout en validant les choix de l'utilisateur grâce à l'injection de dépendances (DI)
La classe SaladChef indique la liste d'ingrédients approuvés sur le vaisseau pour une salade Ten Forward.
SaladChef.cs :
namespace BlazorSample;
public class SaladChef
{
public string[] SaladToppers = { "Horva", "Kanda Root", "Krintar", "Plomeek",
"Syto Bean" };
}
Enregistrez SaladChef dans le conteneur DI de l’application dans le fichier Program :
builder.Services.AddTransient<SaladChef>();
La méthode IsValid de la classe SaladChefValidatorAttribute suivante obtient le service SaladChef à partir de DI pour vérifier l’entrée utilisateur.
SaladChefValidatorAttribute.cs :
using System.ComponentModel.DataAnnotations;
namespace BlazorSample;
public class SaladChefValidatorAttribute : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value,
ValidationContext validationContext)
{
var saladChef = validationContext.GetRequiredService<SaladChef>();
if (saladChef.SaladToppers.Contains(value?.ToString()))
{
return ValidationResult.Success;
}
return new ValidationResult("Is that a Vulcan salad topper?! " +
"The following toppers are available for a Ten Forward salad: " +
string.Join(", ", saladChef.SaladToppers));
}
}
Le composant suivant valide l’entrée utilisateur en appliquant SaladChefValidatorAttribute ([SaladChefValidator]) à la chaîne d’ingrédients de salade (SaladIngredient).
Starship12.razor :
@page "/starship-12"
@inject SaladChef SaladChef
<EditForm Model="this" autocomplete="off" FormName="Starship12">
<DataAnnotationsValidator />
<div>
<label>
Salad topper (@saladToppers):
<input @bind="SaladIngredient" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
private string? saladToppers;
[SaladChefValidator]
public string? SaladIngredient { get; set; }
protected override void OnInitialized() =>
saladToppers ??= string.Join(", ", SaladChef.SaladToppers);
}
@page "/starship-12"
@inject SaladChef SaladChef
<EditForm Model="this" autocomplete="off" FormName="Starship12">
<DataAnnotationsValidator />
<div>
<label>
Salad topper (@saladToppers):
<input @bind="SaladIngredient" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
private string? saladToppers;
[SaladChefValidator]
public string? SaladIngredient { get; set; }
protected override void OnInitialized() =>
saladToppers ??= string.Join(", ", SaladChef.SaladToppers);
}
@page "/starship-12"
@inject SaladChef SaladChef
<EditForm Model="this" autocomplete="off">
<DataAnnotationsValidator />
<p>
<label>
Salad topper (@saladToppers):
<input @bind="SaladIngredient" />
</label>
</p>
<button type="submit">Submit</button>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
private string? saladToppers;
[SaladChefValidator]
public string? SaladIngredient { get; set; }
protected override void OnInitialized() =>
saladToppers ??= string.Join(", ", SaladChef.SaladToppers);
}
Attributs de classe CSS de validation personnalisée
Les attributs de classe CSS de validation personnalisés sont utiles lors de l’intégration avec des frameworks CSS, tels que Bootstrap.
Pour spécifier des attributs de classe CSS de validation personnalisés, commencez par fournir des styles CSS pour la validation personnalisée. Dans l’exemple suivant, les styles valid (validField) et invalid (invalidField) sont spécifiés.
Ajoutez les classes CSS suivantes à la feuille de style de l’application :
.validField {
border-color: lawngreen;
}
.invalidField {
background-color: tomato;
}
Créez une classe dérivée de FieldCssClassProvider qui vérifie les messages de validation des champs et applique le style valid ou invalid approprié.
CustomFieldClassProvider.cs :
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = editContext.IsValid(fieldIdentifier);
return isValid ? "validField" : "invalidField";
}
}
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
return isValid ? "validField" : "invalidField";
}
}
Définissez la classe CustomFieldClassProvider comme fournisseur de classe CSS de champ sur l’instance EditContext du formulaire avec SetFieldCssClassProvider.
Starship13.razor :
@page "/starship-13"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship13> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship13">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider());
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-13"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship13> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship13">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider());
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-13"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship13> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText @bind-Value="Model!.Id" />
<button type="submit">Submit</button>
</EditForm>
@code {
private EditContext? editContext;
public Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider());
}
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
L’exemple précédent vérifie la validité de tous les champs du formulaire et applique un style à chaque champ. Si le formulaire ne doit appliquer des styles personnalisés qu’à un sous-ensemble de champs, faites en sorte que CustomFieldClassProvider applique les styles de manière conditionnelle. L’exemple CustomFieldClassProvider2 suivant applique uniquement un style au champ Name. Pour tous les champs dont le nom ne correspond pas à Name, string.Empty est renvoyé et aucun style n’est appliqué. À l’aide de la réflexion, le champ est mis en correspondance avec la propriété ou le nom de champ du membre du modèle, et non avec un id attribué à l’entité HTML.
CustomFieldClassProvider2.cs :
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider2 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
if (fieldIdentifier.FieldName == "Name")
{
var isValid = editContext.IsValid(fieldIdentifier);
return isValid ? "validField" : "invalidField";
}
return string.Empty;
}
}
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider2 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
if (fieldIdentifier.FieldName == "Name")
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
return isValid ? "validField" : "invalidField";
}
return string.Empty;
}
}
Note
La correspondance du nom de champ dans l’exemple précédent est sensible à la casse. Par conséquent, un membre de la propriété du modèle désigné « Name » doit correspondre à un test conditionnel de « Name ».
-
Correspond correctement :
fieldId.FieldName == "Name" -
Échec de la correspondance :
fieldId.FieldName == "name" -
Échec de la correspondance :
fieldId.FieldName == "NAME" -
Échec de la correspondance :
fieldId.FieldName == "nAmE"
Ajoutez une propriété supplémentaire à Model, par exemple :
[StringLength(10, ErrorMessage = "Description is too long.")]
public string? Description { get; set; }
Ajoutez le Description au formulaire du composant CustomValidationForm :
<InputText @bind-Value="Model!.Description" />
Mettez à jour l’instance EditContext dans la méthode OnInitialized du composant afin d’utiliser le nouveau fournisseur de classes CSS de champ :
editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());
Étant donné qu’aucune classe de validation CSS n’est appliquée au champ Description, celui-ci n’est pas stylisé. Cependant, la validation du champ s’exécute normalement. Si plus de 10 caractères sont fournis, le résumé de validation indique l’erreur :
Description trop longue.
Dans l’exemple suivant :
Le style CSS personnalisé est appliqué au champ
Name.Tous les autres champs appliquent une logique similaire à la logique par défaut de Blazor et utilisent les styles de validation CSS de champ par défaut de Blazor ,
modifiedavecvalidouinvalid. Notez que pour les styles par défaut, vous n’avez pas besoin de les ajouter à la feuille de style de l’application si celle-ci est basée sur un modèle de projet Blazor. Pour les applications qui ne sont pas basées sur un modèle de projet Blazor, les styles par défaut peuvent être ajoutés à la feuille de style de l’application :.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; } .invalid { outline: 1px solid red; }
CustomFieldClassProvider3.cs :
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider3 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = editContext.IsValid(fieldIdentifier);
if (fieldIdentifier.FieldName == "Name")
{
return isValid ? "validField" : "invalidField";
}
else
{
if (editContext.IsModified(fieldIdentifier))
{
return isValid ? "modified valid" : "modified invalid";
}
else
{
return isValid ? "valid" : "invalid";
}
}
}
}
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider3 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
if (fieldIdentifier.FieldName == "Name")
{
return isValid ? "validField" : "invalidField";
}
else
{
if (editContext.IsModified(fieldIdentifier))
{
return isValid ? "modified valid" : "modified invalid";
}
else
{
return isValid ? "valid" : "invalid";
}
}
}
}
Mettez à jour l’instance EditContext dans la méthode OnInitialized du composant afin d’utiliser le fournisseur de classes CSS de champ précédent :
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());
Utilisation de CustomFieldClassProvider3:
- Le champ
Nameutilise les styles CSS de validation personnalisés de l’application. - Le champ
Descriptionutilise une logique similaire à celle de Blazor et les styles de validation CSS de champ par défaut de Blazor.
Validation au niveau de la classe avec IValidatableObject
La validation au niveau de la classe avec IValidatableObject (documentation de l’API) est prise en charge pour les modèles de formulaire Blazor.
IValidatableObjectLa validation ne s’exécute que lorsque le formulaire est soumis et uniquement si toutes les autres validations réussissent
Blazorpackage de validation des annotations de donnée
Note
Le Microsoft.AspNetCore.Components.DataAnnotations.Validation package n’est plus recommandé pour les applications qui ciblent .NET 10 ou version ultérieure. Pour plus d’informations, consultez la section Objets imbriqués, types de collection et types complexes .
Le Microsoft.AspNetCore.Components.DataAnnotations.Validation package remplit les lacunes de l’expérience de validation à l’aide du DataAnnotationsValidator composant. Le package est actuellement en phase expérimentale.
Warning
Le Microsoft.AspNetCore.Components.DataAnnotations.Validation package dispose d’une dernière version de la version candidate à NuGet.org. Continuez à utiliser le package de candidat de mise en production expérimental pour l’instant. Des fonctionnalités expérimentales sont fournies pour explorer la viabilité des fonctionnalités et peuvent ne pas être livrées dans une version stable. Consultez le référentiel GitHub des annonces, le référentiel GitHub dotnet/aspnetcore ou cette rubrique pour obtenir des mises à jour supplémentaires.
Attribut [CompareProperty]
CompareAttribute ne fonctionne pas correctement avec le composant DataAnnotationsValidator, car DataAnnotationsValidator n’associe pas le résultat de la validation à un membre spécifique. Cela peut entraîner un comportement incohérent entre la validation au niveau du champ et la validation de l’ensemble du modèle lors d’une soumission. Le Microsoft.AspNetCore.Components.DataAnnotations.Validation package expérimental introduit un attribut de validation supplémentaire, ComparePropertyAttributequi fonctionne autour de ces limitations. Dans une application Blazor, [CompareProperty] remplace directement l’attribut [Compare].
Objets imbriqués et types de collection
BlazorLa validation de formulaire inclut la prise en charge de la validation des propriétés des objets imbriqués et des éléments de collection avec DataAnnotationsValidator intégré.
Pour créer un formulaire validé, utilisez un DataAnnotationsValidator composant à l’intérieur d’un EditForm composant.
Pour choisir les objets imbriqués et la fonctionnalité de validation des types de collection :
- Appelez la méthode d’extension AddValidation dans le
Programfichier dans lequel les services sont inscrits. - Déclarez les types de modèle de formulaire dans un fichier de classe C#, et non dans un Razor composant (
.razor). - Annotez le type de modèle de formulaire racine avec l’attribut
[ValidatableType].
L’exemple suivant illustre les commandes client avec la validation de formulaire de collecte imbriquée.
Dans Program.cs, appelez AddValidation sur la collection de services :
builder.Services.AddValidation();
Dans la classe suivante Order , l’attribut [ValidatableType] est requis sur le type de modèle de niveau supérieur. Les autres types sont découverts automatiquement.
Order.cs :
using System.ComponentModel.DataAnnotations;
[ValidatableType]
public class Order
{
public Customer Customer { get; set; } = new();
public List<OrderItem> OrderItems { get; set; } = [];
}
public class Customer
{
[Required(ErrorMessage = "Name is required.")]
public string? FullName { get; set; }
[Required(ErrorMessage = "Email is required.")]
public string? Email { get; set; }
public ShippingAddress ShippingAddress { get; set; } = new();
}
OrderItem.cs :
public class OrderItem
{
[Required(ErrorMessage = "Id is required.")]
public int Id { get; set; }
[Required(ErrorMessage = "Description is required.")]
public string? Description { get; set; }
[Required(ErrorMessage = "Price is required.")]
public decimal Price { get; set; }
}
ShippingAddress.cs :
public class ShippingAddress
{
[Required(ErrorMessage = "Street is required.")]
public string? Street { get; set; }
[Required(ErrorMessage = "City is required.")]
public string? City { get; set; }
[Required(ErrorMessage = "State/Province is required.")]
public string? StateProvince { get; set; }
[Required(ErrorMessage = "PostalCode is required.")]
public string? PostalCode { get; set; }
}
Dans le composant suivant OrderPage , le DataAnnotationsValidator composant est présent dans le EditForm composant.
OrderPage.razor :
<EditForm Model="Model">
<DataAnnotationsValidator />
<h3>Customer Details</h3>
<div class="mb-3">
<label>
Full Name
<InputText @bind-Value="Model!.Customer.FullName" />
</label>
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</div>
// ... form continues ...
</EditForm>
@code {
public Order? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
}
L’exigence de déclarer les types de modèle en dehors des composants (Razorfichiers) est due au fait que la fonctionnalité de validation de .razor collection imbriquée et le Razor compilateur lui-même utilisent un générateur source. Actuellement, la sortie d’un générateur source ne peut pas être utilisée comme entrée pour un autre générateur source.
Pour obtenir des conseils sur l’utilisation de modèles de validation à partir d’un autre assembly, consultez la section Utiliser des modèles de validation à partir d’une autre section d’assembly .
Objets imbriqués, types de collection et types complexes
Note
Pour les applications ciblant .NET 10 ou version ultérieure, nous vous déconseillons d’utiliser le package Microsoft.AspNetCore.Components.DataAnnotations.Validation et l’approche décrites dans cette section. Nous vous recommandons d’utiliser les fonctionnalités de validation intégrées du DataAnnotationsValidator composant.
Blazor fournit une prise en charge de la validation des entrées de formulaire à l’aide d’annotations de données avec le DataAnnotationsValidator intégré Toutefois, le DataAnnotationsValidator .NET 9 ou version antérieure valide uniquement les propriétés de niveau supérieur du modèle lié au formulaire qui ne sont pas des propriétés de type collection ou complexe.
Pour valider l’ensemble du graphique objet du modèle lié, y compris les propriétés de type collection et complexe, utilisez le ObjectGraphDataAnnotationsValidator package expérimentalMicrosoft.AspNetCore.Components.DataAnnotations.Validation dans .NET 9 ou version antérieure :
<EditForm ...>
<ObjectGraphDataAnnotationsValidator />
...
</EditForm>
Annoter les propriétés du modèle avec [ValidateComplexType]. Dans les classes de modèle suivantes, la classe ShipDescription contient des annotations de données supplémentaires à valider lorsque le modèle est lié au formulaire :
Starship.cs :
using System;
using System.ComponentModel.DataAnnotations;
public class Starship
{
...
[ValidateComplexType]
public ShipDescription ShipDescription { get; set; } = new();
...
}
ShipDescription.cs :
using System;
using System.ComponentModel.DataAnnotations;
public class ShipDescription
{
[Required]
[StringLength(40, ErrorMessage = "Description too long (40 char).")]
public string? ShortDescription { get; set; }
[Required]
[StringLength(240, ErrorMessage = "Description too long (240 char).")]
public string? LongDescription { get; set; }
}
Utiliser des modèles de validation à partir d’un autre assembly
Pour la validation de modèle définie dans un autre assembly, tel qu'un composant ou un projet .Client de Blazor Web App:
- Si la bibliothèque est une bibliothèque de classe simple (elle n'est pas basée sur les SDKs
Microsoft.NET.Sdk.WebouMicrosoft.NET.Sdk.Razor), ajoutez une référence de package à la bibliothèque pour leMicrosoft.Extensions.Validationpackage NuGet. Des étapes supplémentaires sont requises pour les bibliothèques de classes simples, qui sont décrites plus loin dans cette section. - Créez une méthode dans la bibliothèque ou
.Clientprojet qui reçoit une instance IServiceCollection en tant qu’argument et appelle AddValidation dessus. - Dans l’application, appelez la méthode et AddValidation.
L’approche précédente entraîne la validation des types des deux assemblys.
Dans l’exemple suivant, la AddValidationForTypesInClient méthode est créée pour le projet .Client d'un Blazor Web App pour validation à l’aide de types définis dans le projet .Client.
ServiceCollectionExtensions.cs (dans le .Client projet) :
namespace BlazorSample.Client.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddValidationForTypesInClient(
this IServiceCollection collection)
{
return collection.AddValidation();
}
}
Dans le fichier du projet Program serveur, ajoutez l'espace de noms et appelez la méthode d’extension de la collection de services du projet .Client (AddValidationForTypesInClient) et AddValidation:
using BlazorSample.Client.Extensions;
...
builder.Services.AddValidationForTypesInClient();
builder.Services.AddValidation();
Les nouveaux attributs du Microsoft.Extensions.Validation package (ValidatableTypeAttribute et SkipValidationAttribute) sont publiés comme expérimentaux dans .NET 10. Le package est destiné à fournir une nouvelle infrastructure partagée pour les fonctionnalités de validation entre les frameworks, et les types expérimentaux de publication offrent une plus grande flexibilité pour la conception finale de l’API publique pour une meilleure prise en charge des infrastructures consommatrices.
Dans Blazor les applications, les types sont rendus disponibles via un attribut incorporé généré. Si un projet d’application web qui utilise le Microsoft.NET.Sdk.Web Kit de développement logiciel (<Project Sdk="Microsoft.NET.Sdk.Web">SDK) ou une bibliothèque RCL qui utilise le Microsoft.NET.Sdk.Razor SDK () contient <Project Sdk="Microsoft.NET.Sdk.Razor"> des composants (Razor.razor), l’infrastructure génère automatiquement un attribut interne à l’intérieur du projet (Microsoft.Extensions.Validation.Embedded.ValidatableType, Microsoft.Extensions.Validation.Embedded.SkipValidation). Ces types sont interchangeables avec les attributs réels et ne sont pas marqués comme expérimentaux. Dans la majorité des cas, les développeurs utilisent les [ValidatableType]/[SkipValidation] attributs sur leurs classes sans préoccupation sur leur source.
Toutefois, l’approche précédente n’est pas viable dans les bibliothèques de classes simples qui utilisent le Microsoft.NET.Sdk Kit de développement logiciel (<Project Sdk="Microsoft.NET.Sdk">SDK). L’utilisation des types dans une bibliothèque de classes simples entraîne un avertissement d’analyse du code :
ASP0029 : « Microsoft.Extensions.Validation.ValidateableTypeAttribute » est à des fins d’évaluation uniquement et peut être modifié ou supprimé dans les mises à jour ultérieures. Supprimez ce diagnostic pour continuer.
L’avertissement peut être supprimé à l’aide de l’une des approches suivantes :
Propriété
<NoWarn>dans le fichier projet :<PropertyGroup> <NoWarn>$(NoWarn);ASP0029</NoWarn> </PropertyGroup>Directive
pragmadans laquelle l’attribut est utilisé :#pragma warning disable ASP0029 [Microsoft.Extensions.Validation.ValidatableType] #pragma warning restore ASP0029Règle de fichier EditorConfig (
.editorconfig) :dotnet_diagnostic.ASP0029.severity = none
Si la suppression de l’avertissement n’est pas acceptable, créez manuellement l’attribut incorporé dans la bibliothèque que les kits de développement logiciel (SDK) et Razor Web génèrent automatiquement.
ValidatableTypeAttribute.cs :
namespace Microsoft.Extensions.Validation.Embedded
{
[AttributeUsage(AttributeTargets.Class)]
internal sealed class ValidatableTypeAttribute : Attribute
{
}
}
Utilisez l’espace de noms exact (Microsoft.Extensions.Validation.Embedded) et le nom de classe (ValidatableTypeAttribute) pour que le générateur de source de validation détecte et utilise le type. Vous pouvez déclarer une instruction globale using pour l’espace de noms, soit avec une global using Microsoft.Extensions.Validation.Embedded; instruction, soit avec un <Using Include="Microsoft.Extensions.Validation.Embedded" /> élément dans le fichier projet de la bibliothèque.
Quelle que soit l’approche adoptée, marquez la présence de la solution de contournement pour une future mise à jour de votre code. Les mises à jour du framework pour faciliter l’adoption des types de validation dans les bibliothèques de classes simples sont prévues pour .NET 11 (novembre 2026).
Activer le bouton Soumettre en fonction de la validation du formulaire
Pour activer et désactiver le bouton Soumettre en fonction de la validation du formulaire, utilisez l’exemple suivant :
- Utilise une version raccourcie du formulaire
Starfleet Starship Databaseprécédent (composantStarship3) de la section Formulaire d’exemple de l’article Composants d’entrée qui n’accepte qu’une valeur pour l’ID du vaisseau. Les autres propriétésStarshipreçoivent des valeurs par défaut valides lorsqu’une instance du typeStarshipest créée. - Utilise le EditContext du formulaire pour attribuer le modèle lorsque le composant est initialisé.
- Valide le formulaire dans le callback OnFieldChanged du contexte pour activer et désactiver le bouton « Envoyer ».
- Implémente IDisposable et désabonne le gestionnaire d’événements dans la méthode
Dispose. Pour plus d’informations, consultez Suppression de composants ASP.NET Core Razor.
Note
Lorsque vous affectez une valeur à EditForm.EditContext, n’affectez pas également une valeur EditForm.Model à EditForm.
Starship14.razor :
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship14">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit" disabled="@formInvalid">Submit</button>
</div>
</EditForm>
@code {
private bool formInvalid = false;
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??=
new()
{
Id = "NCC-1701",
Classification = "Exploration",
MaximumAccommodation = 150,
IsValidatedDesign = true,
ProductionDate = new DateTime(2245, 4, 11)
};
editContext = new(Model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (editContext is not null)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public void Dispose()
{
if (editContext is not null)
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship14">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit" disabled="@formInvalid">Submit</button>
</div>
</EditForm>
@code {
private bool formInvalid = false;
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??=
new()
{
Id = "NCC-1701",
Classification = "Exploration",
MaximumAccommodation = 150,
IsValidatedDesign = true,
ProductionDate = new DateTime(2245, 4, 11)
};
editContext = new(Model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (editContext is not null)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public void Dispose()
{
if (editContext is not null)
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit" disabled="@formInvalid">Submit</button>
</div>
</EditForm>
@code {
private bool formInvalid = false;
private EditContext? editContext;
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??=
new()
{
Id = "NCC-1701",
Classification = "Exploration",
MaximumAccommodation = 150,
IsValidatedDesign = true,
ProductionDate = new DateTime(2245, 4, 11)
};
editContext = new(Model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (editContext is not null)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
}
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
Si un formulaire n’est pas préchargé avec des valeurs valides et que vous souhaitez désactiver le bouton Submit lors du chargement du formulaire, définissez formInvalid sur true.
L’approche précédente a pour effet secondaire qu’un résumé de validation (composant ValidationSummary) est rempli avec les champs non valides après que l’utilisateur a interagi avec l’un des champs. Traitez ce scénario de l’une des manières suivantes :
- N’utilisez pas de composant ValidationSummary dans le formulaire.
- Rendez le composant ValidationSummary visible lorsque le bouton Envoyer est sélectionné (par exemple, dans une méthode
Submit).
<EditForm ... EditContext="editContext" OnValidSubmit="Submit" ...>
<DataAnnotationsValidator />
<ValidationSummary style="@displaySummary" />
...
<button type="submit" disabled="@formInvalid">Submit</button>
</EditForm>
@code {
private string displaySummary = "display:none";
...
private void Submit()
{
displaySummary = "display:block";
}
}
DataAnnotationsValidator comportement de validation
Le DataAnnotationsValidator composant a le même ordre de validation et le comportement de court-circuitage que System.ComponentModel.DataAnnotations.Validator. Les règles suivantes sont appliquées lors de la validation d’une instance de type T:
- Les propriétés membres de
Tsont validées, notamment la validation récursive des objets imbriqués. - Les attributs de niveau type de
Tsont validés. - La IValidatableObject.Validate méthode est exécutée, si
Telle l’implémente.
Si l’une des étapes précédentes génère une erreur de validation, les étapes restantes sont ignorées.