Validation des données dans les API minimales .NET 10
[[academy-video-youtube({"vid": "sW_AcN-nD0Y", "start_time": "0", "title": "Validation des données dans .NET 10 Minimal APIs", "creator": "Tim Corey", "length": "~10m"})]]
Les API minimales ont toujours été l'alternative légère aux contrôleurs basés sur ASP.NET Core, mais pendant longtemps, elles avaient un écart notable : pas de support intégré pour valider les données entrantes. Vous aviez soit à faire des vérifications manuelles dans chaque gestionnaire, soit à vous appuyer sur des bibliothèques tierces. .NET 10 comble cet écart avec la validation par annotation de données de première classe pour les chaînes de requête, les en-têtes et les corps des requêtes, le tout sans packages supplémentaires.
Cette plongée approfondie dans les API Minimales .NET 10, basée sur la démonstration de Tim Corey, explique comment enregistrer le service de validation, annoter les classes modèles, et éviter les pièges courants liés aux modificateurs d'accès qui peuvent faire échouer votre configuration.
La Configuration : Une API Minimale Simple
[0:44 - 1:35] La démonstration commence avec un projet API Minimal ASP.NET Core dépouillé fonctionnant sur .NET 10. La surface est intentionnellement petite : deux points de terminaison POST, chacun acceptant un modèle différent.
app.MapPost("/person", (Person person) => Results.Ok(person));
app.MapPost("/login", (LoginModel login) => Results.Ok(login));
app.MapPost("/person", (Person person) => Results.Ok(person));
app.MapPost("/login", (LoginModel login) => Results.Ok(login));
Le modèle Person porte des champs d'identité de base. LoginModel gère les identifiants : une adresse e-mail, un mot de passe et un champ de confirmation de mot de passe. Les deux sont envoyés en tant que corps JSON. À ce stade, il n'y a aucun contrôle des entrées ; l'API accepte tout ce qu'elle reçoit, y compris les chaînes vides et les adresses email mal formées.
Scalar (l'interface utilisateur OpenAPI moderne qui accompagne .NET 10) est utilisé pour envoyer des requêtes de test directement depuis le navigateur, ce qui permet de voir exactement ce que l'API renvoie avant et après la mise en place de la validation.
Enregistrement du Service de Validation
[2:36 - 3:06] Avant que tout attribut de validation ne prenne effet, vous devez opter pour cela au niveau du service. Une seule ligne dans votre bloc d'enregistrement de service permet l'application sur tous les points de terminaison d'API Minimale :
builder.Services.AddValidation();
builder.Services.AddValidation();
Cette seule ligne est l'étape de configuration entière. Il n'y a pas de middleware à ajouter, pas d'étape de pipeline à connecter. Le framework prend automatiquement le relais une fois le service enregistré. Si vous omettez cet appel, vos attributs d'annotation de données seront présents sur le modèle mais jamais évalués, et les requêtes passeront indépendamment de leur contenu.
Cela vaut la peine de garder à l'esprit comme première étape de débogage : si la validation ne fait rien en silence, il manque généralement AddValidation().
Ajouter la Validation à un Modèle de Classe
[3:00 - 3:55] Avec le service enregistré, ajouter la validation à un modèle basé sur une classe consiste à décorer les propriétés avec des attributs d'annotation de données :
public class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
public class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
Après avoir ajouté la directive using System.ComponentModel.DataAnnotations en haut du fichier, marquer FirstName et LastName comme [Required] garantit qu'ils sont validés. Envoyer une requête POST avec un corps vide via Scalar renvoie maintenant un 400 Bad Request avec une réponse d'erreur structurée :
{
"errors": {
"FirstName": ["The FirstName field is required."],
"LastName": ["The LastName field is required."]
}
}
Pas de gestion d'erreur personnalisée, pas d'attributs de filtre. Le framework produit cette réponse automatiquement, et la vérification s'exécute avant que le corps de votre gestionnaire ne s'exécute, donc vous n'avez jamais besoin de vous protéger contre les valeurs nulles à l'intérieur de la logique du point de terminaison.
Appliquer la Validation à un Record
[4:29 - 5:30] Les mêmes attributs fonctionnent sur les enregistrements C#, mais la syntaxe diffère légèrement car les propriétés des enregistrements sont généralement définies dans le constructeur principal plutôt que comme des déclarations de membres séparées.
public record LoginModel(
[Required] [EmailAddress] string Email,
[Required] string Password,
[Required] string ConfirmPassword
);
public record LoginModel(
[Required] [EmailAddress] string Email,
[Required] string Password,
[Required] string ConfirmPassword
);
Les attributs sur les paramètres du constructeur sont appliqués aux propriétés générées, donc [Required] et [EmailAddress] se comportent exactement comme ils le feraient dans une classe. Envoyer une requête avec un email mal formaté, disons "notanemail", renvoie maintenant un 400 qui identifie le champ Email comme invalide.
L'attribut [Compare] peut également être utilisé pour imposer que ConfirmPassword corresponde à Password. Sur un enregistrement, les cibles d'attribut ont besoin d'une indication explicite car le compilateur doit savoir que vous voulez dire le membre généré, pas le paramètre du constructeur lui-même :
[property: Compare(nameof(Password))]
string ConfirmPassword
[property: Compare(nameof(Password))]
string ConfirmPassword
La cible [property:] indique au compilateur d'attacher l'attribut au membre généré plutôt qu'au paramètre. Sans cela, [Compare] compile mais ne s'exécute jamais lors de la vérification. C'est la partie la plus délicate à travailler avec les enregistrements vs classes dans ce contexte : les propriétés de classe acceptent naturellement les attributs, tandis que les paramètres des enregistrements ont besoin de la cible explicite pour tout ce qui fonctionne au niveau des membres.
L'Exigence du Modificateur d'Accès Public
[7:51 - 9:00] Un piège courant est facile à manquer et ne produit aucun message d'erreur lorsque vous le rencontrez. Le système de validation utilise la réflexion pour inspecter vos types de modèles à l'exécution ; pour que la réflexion trouve les membres d'un type, le type lui-même doit être marqué public.
// Validation will NOT run; the class is internal by default
class Person { ... }
// Validation runs correctly
public class Person { ... }
// Validation will NOT run; the class is internal by default
class Person { ... }
// Validation runs correctly
public class Person { ... }
La même règle s'applique aux enregistrements. Si votre modèle est déclaré sans modificateur d'accès, C# par défaut est internal, et le service l'ignore complètement. Votre point de terminaison reçoit toujours la requête, le gestionnaire s'exécute toujours et aucune erreur n'est renvoyée ; les vérifications ne font simplement rien silencieusement.
C'est un comportement ASP.NET Core, non spécifique aux APIs minimales, mais il apparaît plus souvent ici parce que ces projets ont tendance à être compacts et que les développeurs définissent parfois des modèles en ligne ou dans le même fichier que Program.cs sans penser à la visibilité.
Un moyen rapide d'auditer votre projet : tout type de modèle passé à un gestionnaire de point de terminaison doit être explicitement marqué public. Sinon, aucun nombre d'attributs ne fera démarrer le framework.
Ce Que Vous Pouvez Valider
Les attributs d'annotation de données intégrés couvrent les scénarios les plus courants sans aucun travail supplémentaire :
[Required]rejette les valeurs nulles ou vides[EmailAddress]valide le format d'une chaîne d'e-mail[Compare]vérifie que deux propriétés correspondent, utile pour la confirmation de mot de passe[Range]impose des limites numériques ou de date[StringLength]limite la longueur de la chaîne avec un minimum optionnel[RegularExpression]valide selon un modèle personnalisé
Tous ces travaux traversent les paramètres de chaîne de requête, les en-têtes de requête, et les corps JSON. La même classe modèle ou enregistrement peut être lié à partir de différentes sources sans modifier aucun de ses attributs.
Conclusion
[7:46 - fin] Avec AddValidation() enregistré et vos modèles décorés, les APIs minimales appliquent automatiquement les contraintes d'entrée aux classes et enregistrements. Pour faire fonctionner cela dans .NET 10, appelez simplement builder.Services.AddValidation() une fois, décorez les propriétés de votre modèle avec des attributs d'annotation de données standard, et assurez-vous que chaque type de modèle est marqué public.
La cible [property:] sur les enregistrements et l'exigence de modificateur public sont les seules vraies subtilités à surveiller. Ils sont faciles à ignorer et produisent des échecs silencieux plutôt que des erreurs de compilation, alors gardez-les sur votre liste de contrôle chaque fois que les vérifications d'entrée semblent ne rien faire.
Regardez la vidéo complète sur la chaîne de Tim Corey sur YouTube pour une démonstration complète du code source.
