Ce 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.

Télécharger Microsoft Edge Plus d’informations sur Internet Explorer et Microsoft Edge

Cet article explique ce qu’est la liaison de modèle, comment elle fonctionne et comment personnaliser son comportement.

Description de la liaison de modèle

Les contrôleurs et Razor les pages fonctionnent avec des données provenant de requêtes HTTP. Par exemple, les données de routage peuvent fournir une clé d’enregistrement, et les champs de formulaire posté peuvent fournir des valeurs pour les propriétés du modèle. L’écriture du code permettant de récupérer chacune de ces valeurs et de les convertir en types .NET à partir de chaînes est fastidieuse et source d’erreurs. La liaison de modèle automatise ce processus. Le système de liaison de modèle :

  • Récupère les données de diverses sources telles que les données de routage, les champs de formulaire et les chaînes de requête
  • Fournit les données aux contrôleurs et Razor aux pages dans les paramètres de méthode et les propriétés publiques.
  • Convertit les données de chaîne en types .NET
  • Met à jour les propriétés des types complexes
  • Exemple

    Supposons que vous ayez la méthode d’action suivante :

    [HttpGet("{id}")] public ActionResult<Pet> GetById(int id, bool dogsOnly)

    Et que l’application reçoive une requête avec l’URL suivante :

    https://contoso.com/api/pets/2?DogsOnly=true
    

    La liaison de modèle passe par les étapes suivantes une fois que le système de routage a sélectionné la méthode d’action :

  • Elle recherche le premier paramètre de GetById, un entier nommé id.
  • Elle parcourt les sources disponibles dans la requête HTTP et trouve id = « 2 » dans les données de routage.
  • Elle convertit la chaîne « 2 » en entier 2.
  • Elle recherche le paramètre suivant de GetById, un booléen nommé dogsOnly.
  • Elle parcourt les sources et trouve « DogsOnly=true » dans la chaîne de requête. La mise en correspondance des noms ne respecte pas la casse.
  • Elle convertit la chaîne « true » en booléen true.
  • Le framework appelle ensuite la méthode GetById, en passant 2 pour le paramètre id, et true pour le paramètre dogsOnly.

    Dans l’exemple précédent, les cibles de liaison de modèle sont des paramètres de méthode qui sont des types simples . Les cibles peuvent être également les propriétés d’un type complexe. Une fois chaque propriété correctement liée, la validation du modèle est effectuée pour la propriété concernée. L’enregistrement des données liées au modèle (ainsi que des erreurs de liaison ou de validation) est stocké dans ControllerBase.ModelState ou PageModel.ModelState. Pour savoir si ce processus a abouti, l’application vérifie l’indicateur ModelState.IsValid.

    Targets

    La liaison de modèle tente de trouver des valeurs pour les genres de cible suivants :

  • Paramètres de la méthode d’action de contrôleur vers laquelle une requête est routée.
  • Paramètres de la Razor méthode de gestionnaire Pages vers laquelle une requête est acheminée.
  • Propriétés publiques d’un contrôleur ou d’une classe PageModel, si elles sont spécifiées par des attributs.
  • Attribut [BindProperty]

    Peut être appliqué à une propriété publique d’un contrôleur ou à une classe PageModel pour que la liaison de modèle cible cette propriété :

    public class EditModel : PageModel [BindProperty] public Instructor? Instructor { get; set; } // ...

    Attribut [BindProperties]

    Peut être appliqué à un contrôleur ou à une classe PageModel pour indiquer à la liaison de modèle de cibler toutes les propriétés publiques de la classe :

    [BindProperties] public class CreateModel : PageModel public Instructor? Instructor { get; set; } // ...

    Liaison de modèle pour les requêtes HTTP GET

    Par défaut, les propriétés ne sont pas liées pour les requêtes HTTP GET. En règle générale, le paramètre ID d’un enregistrement est tout ce dont vous avez besoin pour une requête GET. L’ID d’enregistrement est utilisé pour rechercher l’élément dans la base de données. Il n’est donc pas nécessaire de lier une propriété qui contient une instance du modèle. Pour les scénarios dans lesquels vous souhaitez que les propriétés soient liées aux données provenant de requêtes GET, affectez à la propriété SupportsGet la valeur true :

    [BindProperty(Name = "ai_user", SupportsGet = true)] public string? ApplicationInsightsCookie { get; set; }

    Types simples et complexes de liaison de modèle

    La liaison de données utilise des définitions spécifiques pour les types sur lesquels elle opère. Un type simple est converti à partir d’une chaîne unique à l’aide TypeConverter d’une méthode ou .TryParse Un type complexe est converti à partir de plusieurs valeurs d’entrée. L’infrastructure détermine la différence en fonction de l’existence d’un TypeConverter ou TryParse. Nous vous recommandons de créer un convertisseur de type ou d’utiliser TryParse pour une string conversion vers SomeType qui ne nécessite pas de ressources externes ou plusieurs entrées.

    Sources

    Par défaut, la liaison de modèle obtient des données sous la forme de paires clé-valeur à partir des sources suivantes dans une requête HTTP :

  • Champs de formulaire
  • Corps de la requête (pour les contrôleurs ayant l’attribut [ApiController].)
  • Données de routage
  • Paramètres de chaîne de requête
  • Fichiers chargés
  • Pour chaque paramètre ou propriété cible, les sources sont analysées dans l’ordre indiqué dans la liste précédente. Il existe quelques exceptions :

  • Les données de routage et les valeurs de chaîne de requête sont utilisées uniquement pour les types simples .
  • Les fichiers chargés sont liés uniquement aux types cibles qui implémentent IFormFile ou IEnumerable<IFormFile>.
  • Si la source par défaut n’est pas correcte, utilisez l’un des attributs suivants pour spécifier la source :

  • [FromQuery] - Obtient les valeurs de la chaîne de requête.
  • [FromRoute] - Obtient des valeurs à partir des données de routage.
  • [FromForm] - Obtient les valeurs des champs de formulaire publiés.
  • [FromBody] - Obtient les valeurs du corps de la requête.
  • [FromHeader] - Obtient les valeurs des en-têtes HTTP.
  • Ces attributs :

  • Sont ajoutés aux propriétés du modèle individuellement et non à la classe de modèle, comme dans l’exemple suivant :

    public class Instructor public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ...
  • Acceptent éventuellement une valeur de nom de modèle dans le constructeur. Cette option est fournie au cas où le nom de propriété ne correspondrait pas à la valeur de la requête. Par exemple, la valeur de la requête peut être un en-tête avec un trait d’union dans son nom, comme dans l’exemple suivant :

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)

    Attribut [FromBody]

    Appliquez l’attribut [FromBody] à un paramètre pour remplir ses propriétés à partir du corps d’une requête HTTP. Le runtime ASP.NET Core délègue la responsabilité de la lecture du corps à un formateur d’entrée. Les formateurs d’entrée sont décrits plus loin dans cet article.

    Lorsque [FromBody] est appliqué à un paramètre de type complexe, tous les attributs de source de liaison appliqués à ses propriétés sont ignorés. Par exemple, l’action suivante Create spécifie que son pet paramètre est rempli à partir du corps :

    public ActionResult<Pet> Create([FromBody] Pet pet)

    La Pet classe spécifie que sa Breed propriété est remplie à partir d’un paramètre de chaîne de requête :

    public class Pet public string Name { get; set; } = null!; [FromQuery] // Attribute is ignored. public string Breed { get; set; } = null!;

    Dans l’exemple précédent :

  • L’attribut [FromQuery] est ignoré.
  • La Breed propriété n’est pas remplie à partir d’un paramètre de chaîne de requête.
  • Les formateurs d’entrée lisent uniquement le corps et ne comprennent pas les attributs de la source de liaison. Si une valeur appropriée est trouvée dans le corps, cette valeur est utilisée pour remplir la Breed propriété.

    N’appliquez pas [FromBody] à plus d’un paramètre par méthode d’action. Une fois le flux de requête lu par un formateur d’entrée, il n’est plus disponible pour être lu à nouveau pour la liaison d’autres [FromBody] paramètres.

    Sources supplémentaires

    Les données sources sont fournies au système de liaison de modèle par les fournisseurs de valeurs. Vous pouvez écrire et inscrire des fournisseurs de valeurs personnalisés qui obtiennent des données de liaison de modèle à partir d’autres sources. Par exemple, vous souhaiterez peut-être obtenir des données provenant de l’état de session ou de cookiela session. Pour obtenir des données provenant d’une nouvelle source :

  • Créez une classe qui implémente IValueProvider.
  • Créez une classe qui implémente IValueProviderFactory.
  • Inscrivez la classe de fabrique dans Program.cs.
  • L’exemple inclut un fournisseur de valeurs et un exemple de fabrique qui obtient des valeurs de cookies. Inscrire des fabriques de fournisseurs de valeurs personnalisées dans Program.cs:

    builder.Services.AddControllers(options => options.ValueProviderFactories.Add(new CookieValueProviderFactory());

    Le code précédent place le fournisseur de valeur personnalisé après tous les fournisseurs de valeurs intégrés. Pour en faire le premier fournisseur de la liste, appelez Insert(0, new CookieValueProviderFactory()) à la place de Add.

    Aucune source pour une propriété de modèle

    Par défaut, aucune erreur d’état de modèle n’est créée, s’il n’existe aucune valeur de propriété de modèle. La propriété a une valeur null ou une valeur par défaut :

  • Les types simples nullables sont définis sur null.
  • Les types valeur non Nullable ont la valeur default(T). Par exemple, un paramètre int id a la valeur 0.
  • Pour les types complexes, la liaison de modèle crée une instance à l’aide du constructeur par défaut, sans définir de propriétés.
  • Les tableaux ont la valeur Array.Empty<T>(), sauf les tableaux byte[] qui ont une valeur null.
  • Si l’état du modèle doit être invalidé lorsque rien n’est trouvé dans les champs de formulaire d’une propriété de modèle, utilisez l’attribut [BindRequired] .

    Notez que ce [BindRequired] comportement s’applique à la liaison de modèle à partir de données de formulaire publiées, et non aux JSdonnées ON ou XML dans un corps de requête. Les données du corps de requête sont prises en charge par les formateurs d’entrée.

    Erreurs de conversion de type

    Si une source est localisée mais qu’elle ne peut pas être convertie vers le type cible, l’état du modèle est marqué comme étant non valide. Le paramètre ou la propriété cible a une valeur null ou une valeur par défaut, comme indiqué dans la section précédente.

    Dans un contrôleur d’API ayant l’attribut [ApiController], un état de modèle non valide entraîne une réponse HTTP 400 automatique.

    Dans une Razor page, réafficher la page avec un message d’erreur :

    public IActionResult OnPost() if (!ModelState.IsValid) return Page(); // ... return RedirectToPage("./Index");

    Lorsque la page est réaffichée par le code précédent, l’entrée non valide n’apparaît pas dans le champ de formulaire. En effet, la propriété de modèle à une valeur null ou une valeur par défaut. L’entrée non valide apparaît dans un message d’erreur. Si vous souhaitez réafficher les données incorrectes dans le champ de formulaire, envisagez de faire de la propriété de modèle une chaîne et d’effectuer la conversion de données manuellement.

    La même stratégie est recommandée si vous ne souhaitez pas que les erreurs de conversion de type entraînent des erreurs d’état de modèle. Dans ce cas, transformez la propriété de modèle en chaîne.

    Types simples

    Consultez Types simples et complexes de liaison de modèle pour obtenir une explication des types simples et complexes.

    Les types simples que le lieur de modèle peut convertir en chaînes sources sont les suivants :

  • Booléen
  • Byte, SByte
  • DateOnly
  • DateTime
  • DateTimeOffset
  • Décimal
  • Double
  • Int16, Int32, Int64
  • Unique
  • TimeOnly
  • TimeSpan
  • UInt16, UInt32, UInt64
  • Version
  • Lier avec IParsable<T>.TryParse

    L’API IParsable<TSelf>.TryParse prend en charge les valeurs de paramètre d’action du contrôleur de liaison :

    public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
    

    La classe suivante DateRange implémente pour prendre en IParsable<TSelf> charge la liaison d’une plage de dates :

    public class DateRange : IParsable<DateRange> public DateOnly? From { get; init; } public DateOnly? To { get; init; } public static DateRange Parse(string value, IFormatProvider? provider) if (!TryParse(value, provider, out var result)) throw new ArgumentException("Could not parse supplied value.", nameof(value)); return result; public static bool TryParse(string? value, IFormatProvider? provider, out DateRange dateRange) var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (segments?.Length == 2 && DateOnly.TryParse(segments[0], provider, out var fromDate) && DateOnly.TryParse(segments[1], provider, out var toDate)) dateRange = new DateRange { From = fromDate, To = toDate }; return true; dateRange = new DateRange { From = default, To = default }; return false;

    Le code précédent :

  • Convertit une chaîne représentant deux dates en objet DateRange
  • Le classeur de modèles utilise la IParsable<TSelf>.TryParse méthode pour lier le DateRange.
  • L’action de contrôleur suivante utilise la DateRange classe pour lier une plage de dates :

    // GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022 public IActionResult ByRange([FromQuery] DateRange range) if (!ModelState.IsValid) return View("Error", ModelState.Values.SelectMany(v => v.Errors)); var weatherForecasts = Enumerable .Range(1, 5).Select(index => new WeatherForecast Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From && DateOnly.FromDateTime(wf.Date) <= range.To) .Select(wf => new WeatherForecastViewModel Date = wf.Date.ToString("d"), TemperatureC = wf.TemperatureC, TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556), Summary = wf.Summary return View("Index", weatherForecasts);

    La classe suivante Locale implémente pour prendre en charge la IParsable<TSelf> liaison à CultureInfo:

    public class Locale : CultureInfo, IParsable<Locale> public Locale(string culture) : base(culture) public static Locale Parse(string value, IFormatProvider? provider) if (!TryParse(value, provider, out var result)) throw new ArgumentException("Could not parse supplied value.", nameof(value)); return result; public static bool TryParse([NotNullWhen(true)] string? value, IFormatProvider? provider, out Locale locale) if (value is null) locale = new Locale(CurrentCulture.Name); return false; locale = new Locale(value); return true; catch (CultureNotFoundException) locale = new Locale(CurrentCulture.Name); return false;

    L’action de contrôleur suivante utilise la Locale classe pour lier une CultureInfo chaîne :

    // GET /en-GB/WeatherForecast public IActionResult Index([FromRoute] Locale locale) var weatherForecasts = Enumerable .Range(1, 5).Select(index => new WeatherForecast Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] .Select(wf => new WeatherForecastViewModel Date = wf.Date.ToString("d", locale), TemperatureC = wf.TemperatureC, TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556), Summary = wf.Summary return View(weatherForecasts);

    L’action de contrôleur suivante utilise les DateRange classes et Locale pour lier une plage de dates à CultureInfo:

    // GET /af-ZA/WeatherForecast/RangeByLocale?range=2022-07-24,2022-07-29 public IActionResult RangeByLocale([FromRoute] Locale locale, [FromQuery] string range) if (!ModelState.IsValid) return View("Error", ModelState.Values.SelectMany(v => v.Errors)); if (!DateRange.TryParse(range, locale, out DateRange rangeResult)) ModelState.TryAddModelError(nameof(range), $"Invalid date range: {range} for locale {locale.DisplayName}"); return View("Error", ModelState.Values.SelectMany(v => v.Errors)); var weatherForecasts = Enumerable .Range(1, 5).Select(index => new WeatherForecast Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] .Where(wf => DateOnly.FromDateTime(wf.Date) >= rangeResult.From && DateOnly.FromDateTime(wf.Date) <= rangeResult.To) .Select(wf => new WeatherForecastViewModel Date = wf.Date.ToString("d", locale), TemperatureC = wf.TemperatureC, TemperatureF = 32 + (int) (wf.TemperatureC / 0.5556), Summary = wf.Summary return View("Index", weatherForecasts);

    L’exemple d’application API sur GitHub montre l’exemple précédent pour un contrôleur d’API.

    Lier avec TryParse

    L’API TryParse prend en charge les valeurs de paramètre d’action du contrôleur de liaison :

    public static bool TryParse(string value, T out result);
    public static bool TryParse(string value, IFormatProvider provider, T out result);
    

    IParsable<T>.TryParse est l’approche recommandée pour la liaison de paramètres, car contrairement à TryParse, elle ne dépend pas de la réflexion.

    La classe suivante DateRangeTP implémente TryParse:

    public class DateRangeTP public DateOnly? From { get; } public DateOnly? To { get; } public DateRangeTP(string from, string to) if (string.IsNullOrEmpty(from)) throw new ArgumentNullException(nameof(from)); if (string.IsNullOrEmpty(to)) throw new ArgumentNullException(nameof(to)); From = DateOnly.Parse(from); To = DateOnly.Parse(to); public static bool TryParse(string? value, out DateRangeTP? result) var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (range?.Length != 2) result = default; return false; result = new DateRangeTP(range[0], range[1]); return true;

    L’action de contrôleur suivante utilise la DateRangeTP classe pour lier une plage de dates :

    // GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022 public IActionResult ByRangeTP([FromQuery] DateRangeTP range) if (!ModelState.IsValid) return View("Error", ModelState.Values.SelectMany(v => v.Errors)); var weatherForecasts = Enumerable .Range(1, 5).Select(index => new WeatherForecast Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From && DateOnly.FromDateTime(wf.Date) <= range.To) .Select(wf => new WeatherForecastViewModel Date = wf.Date.ToString("d"), TemperatureC = wf.TemperatureC, TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556), Summary = wf.Summary return View("Index", weatherForecasts);

    Types complexes

    Un type complexe doit avoir un constructeur public par défaut et des propriétés publiques accessibles en écriture à lier. Quand la liaison de modèle se produit, la classe est instanciée à l’aide du constructeur public par défaut.

    Pour chaque propriété du type complexe, la liaison de modèle recherche dans les sources le modèle de nomprefix.property_name. Si rien n’est trouvé, elle recherche uniquement nom_propriété sans le préfixe. La décision d’utiliser le préfixe n’est pas prise par propriété. Par exemple, avec une requête contenant ?Instructor.Id=100&Name=foo, liée à la méthode OnGet(Instructor instructor), l’objet résultant de type Instructor contient :

  • Id défini sur 100.
  • Name défini sur null. La liaison de modèle s’attend Instructor.Name à ce qu’elle ait Instructor.Id été utilisée dans le paramètre de requête précédent.
  • Dans le cas d’une liaison à un paramètre, le préfixe représente le nom du paramètre. Dans le cas d’une liaison à une propriété publique PageModel, le préfixe représente le nom de la propriété publique. Certains attributs ont une propriété Prefix qui vous permet de remplacer l’utilisation par défaut du nom de paramètre ou de propriété.

    Par exemple, supposons que le type complexe corresponde à la classe Instructor suivante :

    public class Instructor
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
    

    Préfixe = nom de paramètre

    Si le modèle à lier est un paramètre nommé instructorToUpdate :

    public IActionResult OnPost(int? id, Instructor instructorToUpdate)
    

    La liaison de modèle commence par rechercher dans les sources la clé instructorToUpdate.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Préfixe = nom de propriété

    Si le modèle à lier est une propriété nommée Instructor du contrôleur ou de la classe PageModel :

    [BindProperty]
    public Instructor Instructor { get; set; }
    

    La liaison de modèle commence par rechercher dans les sources la clé Instructor.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Préfixe personnalisé

    Si le modèle à lier est un paramètre nommé instructorToUpdate et si un attribut Bind spécifie Instructor en tant que préfixe :

    public IActionResult OnPost(
        int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
    

    La liaison de modèle commence par rechercher dans les sources la clé Instructor.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Attributs des cibles de type complexe

    Plusieurs attributs intégrés sont disponibles pour contrôler la liaison de modèle des types complexes :

  • [Bind]
  • [BindRequired]
  • [BindNever]
  • Avertissement

    Ces attributs affectent la liaison de modèle quand les données de formulaire postées représentent la source des valeurs. Elles n’affectent pas les formateurs d’entrée, qui traitent les corps de requêtes ON et XML publiés JS. Les formateurs d’entrée sont décrits plus loin dans cet article.

    Attribut [Bind]

    Il peut être appliqué à une classe ou à un paramètre de méthode. Il spécifie les propriétés d’un modèle à inclure dans la liaison de modèle. [Bind]n’affecte pas les formateurs d’entrée.

    Dans l’exemple suivant, seules les propriétés spécifiées du modèle Instructor sont liées quand une méthode de gestionnaire ou une méthode d’action est appelée :

    [Bind("LastName,FirstMidName,HireDate")]
    public class Instructor
    

    Dans l’exemple suivant, seules les propriétés spécifiées du modèle Instructor sont liées quand la méthode OnPost est appelée :

    [HttpPost]
    public IActionResult OnPost(
        [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
    

    Vous pouvez utiliser l’attribut [Bind] pour éviter le surpostage dans les scénarios de création. Il ne fonctionne pas bien dans les scénarios de modification, car les propriétés exclues ont une valeur null ou une valeur par défaut au lieu de rester inchangées. Pour empêcher le surpostage, il est recommandé d’utiliser des modèles de vues à la place de l’attribut [Bind]. Pour plus d’informations, consultez Remarque sur la sécurité concernant le surpostage.

    Attribut [ModelBinder]

    ModelBinderAttribute peut être appliqué à des types, des propriétés ou des paramètres. Il permet de spécifier le type de classeur de modèles utilisé pour lier l’instance ou le type spécifique. Par exemple :

    [HttpPost]
    public IActionResult OnPost(
        [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
    

    L’attribut [ModelBinder] peut également être utilisé pour modifier le nom d’une propriété ou d’un paramètre lorsqu’elle est liée au modèle :

    public class Instructor
        [ModelBinder(Name = "instructor_id")]
        public string Id { get; set; }
        // ...
    

    Attribut [BindRequired]

    Il oblige la liaison de modèle à ajouter une erreur d’état de modèle si la liaison est impossible pour la propriété d’un modèle. Voici un exemple :

    public class InstructorBindRequired // ... [BindRequired] public DateTime HireDate { get; set; }

    Consultez également la discussion sur l’attribut [Required] dans Validation de modèle.

    Attribut [BindNever]

    Peut être appliqué à une propriété ou à un type. Il empêche la liaison de modèle de définir la propriété d’un modèle. Lorsqu’il est appliqué à un type, le système de liaison de modèle exclut toutes les propriétés définies par le type. Voici un exemple :

    public class InstructorBindNever [BindNever] public int Id { get; set; } // ...

    Collections

    Pour les cibles qui sont des collections de types simples, la liaison de modèle recherche les correspondances avec nom_paramètre ou nom_propriété. Si aucune correspondance n’est localisée, elle recherche l’un des formats pris en charge sans le préfixe. Par exemple :

  • Supposons que le paramètre à lier soit un tableau nommé selectedCourses :

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Les données de formulaire ou de chaîne de requête peuvent avoir l’un des formats suivants :

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Évitez de lier un paramètre ou une propriété nommée index ou Index s’il est adjacent à une valeur de collection. La liaison de modèle tente d’utiliser index comme index pour la collection, ce qui peut entraîner une liaison incorrecte. Par exemple, considérez l’action suivante :

    public IActionResult Post(string index, List<Product> products)
    

    Dans le code précédent, le paramètre de chaîne de index requête est lié au paramètre de méthode index et est également utilisé pour lier la collection product. Le fait de renommer le index paramètre ou d’utiliser un attribut de liaison de modèle pour configurer la liaison évite ce problème :

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Le format suivant est pris en charge uniquement dans les données de formulaire :

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pour tous les exemples de formats précédents, la liaison de modèle passe un tableau de deux éléments au paramètre selectedCourses :

  • selectedCourses[0]=1050
  • selectedCourses[1]=2000
  • Les formats de données qui utilisent des nombres en indice (... [0] ... [1] ...) doivent être impérativement numérotés de manière séquentielle à partir de zéro. S’il existe des vides dans la numérotation en indice, tous les éléments suivants sont ignorés. Par exemple, si les indices sont 0 et 2 au lieu de 0 et 1, le second élément est ignoré.

    Dictionnaires

    Pour les cibles Dictionary, la liaison de modèle recherche les correspondances avec nom_paramètre ou nom_propriété. Si aucune correspondance n’est localisée, elle recherche l’un des formats pris en charge sans le préfixe. Par exemple :

  • Supposons que le paramètre cible soit un Dictionary<int, string> nommé selectedCourses :

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Les données de chaîne de requête ou de formulaire posté peuvent ressembler à l’un des exemples suivants :

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • Pour tous les exemples de formats précédents, la liaison de modèle passe un dictionnaire de deux éléments au paramètre selectedCourses :

  • selectedCourses["1050"]="Chemistry"
  • selectedCourses["2000"]="Economics"
  • Liaison de constructeur et types d’enregistrements

    La liaison de modèle nécessite que les types complexes aient un constructeur sans paramètre. Les System.Text.Json formateurs d’entrée basés sur et Newtonsoft.Json prennent en charge la désérialisation des classes qui n’ont pas de constructeur sans paramètre.

    Les types d’enregistrements sont un excellent moyen de représenter succinctement des données sur le réseau. ASP.NET Core prend en charge la liaison de modèle et la validation des types d’enregistrements avec un seul constructeur :

    public record Person(
        [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);
    public class PersonController
        public IActionResult Index() => View();
        [HttpPost]
        public IActionResult Index(Person person)
            // ...
    

    Person/Index.cshtml:

    @model Person
    Name: <input asp-for="Name" />
    Age: <input asp-for="Age" />
    

    Lors de la validation des types d’enregistrements, le runtime recherche les métadonnées de liaison et de validation spécifiquement sur les paramètres plutôt que sur les propriétés.

    L’infrastructure permet la liaison aux types d’enregistrements et la validation de ces types :

    public record Person([Required] string Name, [Range(0, 100)] int Age);
    

    Pour que le précédent fonctionne, le type doit :

  • Être un type d’enregistrement.
  • Avoir exactement un constructeur public.
  • Contiennent des paramètres qui ont une propriété avec le même nom et le même type. Les noms ne doivent pas différer selon la casse.
  • POCOs sans constructeurs sans paramètre

    Les POCOs qui n’ont pas de constructeurs sans paramètre ne peuvent pas être liés.

    Le code suivant génère une exception indiquant que le type doit avoir un constructeur sans paramètre :

    public class Person(string Name)
    public record Person([Required] string Name, [Range(0, 100)] int Age)
        public Person(string Name) : this (Name, 0);
    

    Types d’enregistrements avec des constructeurs créés manuellement

    Types d’enregistrements avec des constructeurs créés manuellement qui ressemblent au travail des constructeurs principaux

    public record Person
        public Person([Required] string Name, [Range(0, 100)] int Age)
            => (this.Name, this.Age) = (Name, Age);
        public string Name { get; set; }
        public int Age { get; set; }
    

    Types d’enregistrements, métadonnées de validation et de liaison

    Pour les types d’enregistrements, les métadonnées de validation et de liaison sur les paramètres sont utilisées. Toutes les métadonnées sur les propriétés sont ignorées

    public record Person (string Name, int Age)
       [BindProperty(Name = "SomeName")] // This does not get used
       [Required] // This does not get used
       public string Name { get; init; }
    

    Validation et métadonnées

    La validation utilise des métadonnées sur le paramètre, mais utilise la propriété pour lire la valeur. Dans le cas ordinaire avec les constructeurs principaux, les deux seraient identiques. Toutefois, il existe des moyens de la vaincre :

    public record Person([Required] string Name)
        private readonly string _name;
        // The following property is never null.
        // However this object could have been constructed as "new Person(null)".
        public string Name { get; init => _name = value ?? string.Empty; }
    

    TryUpdateModel ne met pas à jour les paramètres sur un type d’enregistrement

    public record Person(string Name)
        public int Age { get; set; }
    var person = new Person("initial-name");
    TryUpdateModel(person, ...);
    

    Dans ce cas, MVC ne tente pas de lier Name à nouveau. Toutefois, Age est autorisé à être mis à jour

    Comportement de globalisation des données de routage et des chaînes de requête de liaison de modèle

    Le ASP.NET Core fournisseur de valeur de routage et le fournisseur de valeur de chaîne de requête :

  • Traitez les valeurs comme une culture invariante.
  • Attendez-vous à ce que les URL soient indifférentes à la culture.
  • En revanche, les valeurs provenant de données de formulaire subissent une conversion sensible à la culture. Cela est conçu pour que les URL soient partageables entre les paramètres régionaux.

    Pour que le ASP.NET Core route le fournisseur de valeur et le fournisseur de valeurs de chaîne de requête subissent une conversion sensible à la culture :

  • Héritent de IValueProviderFactory
  • Copiez le code de QueryStringValueProviderFactory ou RouteValueValueProviderFactory
  • Remplacez la valeur de culture passée au constructeur du fournisseur de valeurs par CultureInfo.CurrentCulture
  • Remplacez la fabrique de fournisseur de valeurs par défaut dans les options MVC par votre nouvelle :
  • public class CultureQueryStringValueProviderFactory : IValueProviderFactory public Task CreateValueProviderAsync(ValueProviderFactoryContext context) _ = context ?? throw new ArgumentNullException(nameof(context)); var query = context.ActionContext.HttpContext.Request.Query; if (query?.Count > 0) context.ValueProviders.Add( new QueryStringValueProvider( BindingSource.Query, query, CultureInfo.CurrentCulture)); return Task.CompletedTask; builder.Services.AddControllers(options => var index = options.ValueProviderFactories.IndexOf( options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>() .Single()); options.ValueProviderFactories[index] = new CultureQueryStringValueProviderFactory();

    Types de données spéciaux

    Certains types de données spéciaux peuvent être pris en charge par la liaison de modèle.

    IFormFile et IFormFileCollection

    Fichier chargé inclus dans la requête HTTP. IEnumerable<IFormFile> est également pris en charge pour plusieurs fichiers.

    CancellationToken

    Les actions peuvent éventuellement lier un CancellationToken en tant que paramètre. Cette liaison indique RequestAborted quand la connexion sous-jacente à la requête HTTP est abandonnée. Les actions peuvent utiliser ce paramètre pour annuler les opérations asynchrones de longue durée exécutées dans le cadre des actions du contrôleur.

    FormCollection

    Permet de récupérer toutes les valeurs des données de formulaire posté.

    Formateurs d’entrée

    Les données du corps de la demande peuvent être au JSformat ON, XML ou autre. Pour analyser ces données, la liaison de modèle utilise un formateur d’entrée configuré pour prendre en charge un type de contenu particulier. Par défaut, ASP.NET Core inclut des JSformateurs d’entrée basés sur ON pour la gestion des JSdonnées ON. Vous pouvez ajouter d’autres formateurs pour d’autres types de contenu.

    ASP.NET Core sélectionne les formateurs d’entrée en fonction de l’attribut Consumes. Si aucun attribut n’est présent, il utilise l’en-tête Content-Type.

    Pour utiliser les formateurs d’entrée XML intégrés :

  • Dans Program.cs, appelez AddXmlSerializerFormatters ou AddXmlDataContractSerializerFormatters.

    builder.Services.AddControllers() .AddXmlSerializerFormatters();
  • Appliquez l’attribut Consumes aux classes de contrôleur ou aux méthodes d’action devant contenir des données XML dans le corps de la requête.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    Pour plus d’informations, consultez Introduction à la sérialisation XML.

    Personnaliser la liaison de modèle avec des formateurs d’entrée

    Un formateur d’entrée assume l’entière responsabilité de la lecture des données du corps de la demande. Pour personnaliser ce processus, configurez les API utilisées par le formateur d’entrée. Cette section explique comment personnaliser le System.Text.Jsonformateur d’entrée basé sur pour comprendre un type personnalisé nommé ObjectId.

    Considérez le modèle suivant, qui contient une propriété personnalisée ObjectId nommée Id:

    public class InstructorObjectId [Required] public ObjectId ObjectId { get; set; } = null!;

    Pour personnaliser le processus de liaison de modèle lors de l’utilisation de System.Text.Json, créez une classe dérivée de JsonConverter<T>:

    internal class ObjectIdConverter : JsonConverter<ObjectId> public override ObjectId Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => new(JsonSerializer.Deserialize<int>(ref reader, options)); public override void Write( Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options) => writer.WriteNumberValue(value.Id);

    Pour utiliser un convertisseur personnalisé, appliquez l’attribut JsonConverterAttribute au type. Dans l’exemple suivant, le ObjectId type est configuré avec ObjectIdConverter comme convertisseur personnalisé :

    [JsonConverter(typeof(ObjectIdConverter))] public record ObjectId(int Id);

    Pour plus d’informations, consultez Comment écrire des convertisseurs personnalisés.

    Exclure les types spécifiés de la liaison de modèle

    Le comportement des systèmes de liaison et de validation de modèle est piloté par ModelMetadata. Vous pouvez personnaliser ModelMetadata en ajoutant un fournisseur de détails à MvcOptions.ModelMetadataDetailsProviders. Des fournisseurs de détails intégrés sont disponibles pour désactiver la liaison de modèle ou la validation des types spécifiés.

    Pour désactiver la liaison de modèle sur tous les modèles d’un type spécifique, ajoutez ExcludeBindingMetadataProvider dans Program.cs. Par exemple, pour désactiver la liaison de modèle sur tous les modèles de type System.Version :

    builder.Services.AddRazorPages() .AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(Guid)));

    Pour désactiver la validation des propriétés d’un type spécifique, ajoutez SuppressChildValidationMetadataProvider dans Program.cs. Par exemple, pour désactiver la validation sur les propriétés de type System.Guid :

    builder.Services.AddRazorPages() .AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(Guid)));

    Lieurs de modèles personnalisés

    Vous pouvez étendre la liaison de modèle en écrivant un lieur de modèle personnalisé et en utilisant l’attribut [ModelBinder] afin de le sélectionner pour une cible donnée. Découvrez plus d’informations sur la liaison de modèle personnalisée.

    Liaison de modèle manuelle

    Vous pouvez appeler la liaison de modèle manuellement à l’aide de la méthode TryUpdateModelAsync. La méthode est définie sur les classes ControllerBase et PageModel. Les surcharges de méthode vous permettent de spécifier le préfixe et le fournisseur de valeurs à utiliser. La méthode retourne false en cas d’échec de la liaison de modèle. Voici un exemple :

    if (await TryUpdateModelAsync( newInstructor, "Instructor", x => x.Name, x => x.HireDate!)) _instructorStore.Add(newInstructor); return RedirectToPage("./Index"); return Page();

    TryUpdateModelAsync utilise des fournisseurs de valeurs pour obtenir des données à partir du corps du formulaire, de la chaîne de requête et des données de routage. TryUpdateModelAsync est généralement :

  • Utilisé avec les Razor applications Pages et MVC utilisant des contrôleurs et des vues pour empêcher la sur-publication.
  • Non utilisé avec une API web, sauf si consommé à partir de données de formulaire, de chaînes de requête et de données de routage. Les points de terminaison d’API web qui consomment JSON utilisent des formateurs d’entrée pour désérialiser le corps de la requête dans un objet.
  • Pour plus d’informations, consultez TryUpdateModelAsync.

    Attribut [FromServices]

    Le nom de cet attribut suit le modèle des attributs de liaison de modèle qui spécifient une source de données. Toutefois, il ne permet pas de lier les données d’un fournisseur de valeurs. Il obtient une instance d’un type à partir du conteneur d’injection de dépendances. Son objectif est de fournir une alternative à l’injection de constructeurs quand vous avez besoin d’un service uniquement si une méthode particulière est appelée.

    Si une instance du type n’est pas inscrite dans le conteneur d’injection de dépendances, l’application lève une exception lors de la tentative de liaison du paramètre. Pour rendre le paramètre facultatif, utilisez l’une des approches suivantes :

  • Rendez le paramètre nullable.
  • Définissez une valeur par défaut pour le paramètre .
  • Pour les paramètres nullables, vérifiez que le paramètre n’est pas null avant d’y accéder.

    Ressources supplémentaires

  • Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
  • Validation de modèle dans ASP.NET Core MVC
  • Liaison de données personnalisée dans ASP.NET Core
  • Cet article explique ce qu’est la liaison de modèle, comment elle fonctionne et comment personnaliser son comportement.

    Description de la liaison de modèle

    Les contrôleurs et Razor les pages fonctionnent avec des données provenant de requêtes HTTP. Par exemple, les données de routage peuvent fournir une clé d’enregistrement, et les champs de formulaire posté peuvent fournir des valeurs pour les propriétés du modèle. L’écriture du code permettant de récupérer chacune de ces valeurs et de les convertir en types .NET à partir de chaînes est fastidieuse et source d’erreurs. La liaison de modèle automatise ce processus. Le système de liaison de modèle :

  • Récupère les données de diverses sources telles que les données de routage, les champs de formulaire et les chaînes de requête
  • Fournit les données aux contrôleurs et Razor aux pages dans les paramètres de méthode et les propriétés publiques.
  • Convertit les données de chaîne en types .NET
  • Met à jour les propriétés des types complexes
  • Exemple

    Supposons que vous ayez la méthode d’action suivante :

    [HttpGet("{id}")] public ActionResult<Pet> GetById(int id, bool dogsOnly)

    Et que l’application reçoive une requête avec l’URL suivante :

    https://contoso.com/api/pets/2?DogsOnly=true
    

    La liaison de modèle passe par les étapes suivantes une fois que le système de routage a sélectionné la méthode d’action :

  • Elle recherche le premier paramètre de GetById, un entier nommé id.
  • Elle parcourt les sources disponibles dans la requête HTTP et trouve id = « 2 » dans les données de routage.
  • Elle convertit la chaîne « 2 » en entier 2.
  • Elle recherche le paramètre suivant de GetById, un booléen nommé dogsOnly.
  • Elle parcourt les sources et trouve « DogsOnly=true » dans la chaîne de requête. La mise en correspondance des noms ne respecte pas la casse.
  • Elle convertit la chaîne « true » en booléen true.
  • Le framework appelle ensuite la méthode GetById, en passant 2 pour le paramètre id, et true pour le paramètre dogsOnly.

    Dans l’exemple précédent, les cibles de liaison de modèle sont des paramètres de méthode qui sont des types simples. Les cibles peuvent être également les propriétés d’un type complexe. Une fois chaque propriété correctement liée, la validation du modèle est effectuée pour la propriété concernée. L’enregistrement des données liées au modèle (ainsi que des erreurs de liaison ou de validation) est stocké dans ControllerBase.ModelState ou PageModel.ModelState. Pour savoir si ce processus a abouti, l’application vérifie l’indicateur ModelState.IsValid.

    Targets

    La liaison de modèle tente de trouver des valeurs pour les genres de cible suivants :

  • Paramètres de la méthode d’action de contrôleur vers laquelle une requête est routée.
  • Paramètres de la Razor méthode de gestionnaire Pages vers laquelle une requête est acheminée.
  • Propriétés publiques d’un contrôleur ou d’une classe PageModel, si elles sont spécifiées par des attributs.
  • Attribut [BindProperty]

    Peut être appliqué à une propriété publique d’un contrôleur ou à une classe PageModel pour que la liaison de modèle cible cette propriété :

    public class EditModel : PageModel [BindProperty] public Instructor? Instructor { get; set; } // ...

    Attribut [BindProperties]

    Peut être appliqué à un contrôleur ou à une classe PageModel pour indiquer à la liaison de modèle de cibler toutes les propriétés publiques de la classe :

    [BindProperties] public class CreateModel : PageModel public Instructor? Instructor { get; set; } // ...

    Liaison de modèle pour les requêtes HTTP GET

    Par défaut, les propriétés ne sont pas liées pour les requêtes HTTP GET. En règle générale, le paramètre ID d’un enregistrement est tout ce dont vous avez besoin pour une requête GET. L’ID d’enregistrement est utilisé pour rechercher l’élément dans la base de données. Il n’est donc pas nécessaire de lier une propriété qui contient une instance du modèle. Pour les scénarios dans lesquels vous souhaitez que les propriétés soient liées aux données provenant de requêtes GET, affectez à la propriété SupportsGet la valeur true :

    [BindProperty(Name = "ai_user", SupportsGet = true)] public string? ApplicationInsightsCookie { get; set; }

    Sources

    Par défaut, la liaison de modèle obtient des données sous la forme de paires clé-valeur à partir des sources suivantes dans une requête HTTP :

  • Champs de formulaire
  • Corps de la requête (pour les contrôleurs ayant l’attribut [ApiController].)
  • Données de routage
  • Paramètres de chaîne de requête
  • Fichiers chargés
  • Pour chaque paramètre ou propriété cible, les sources sont analysées dans l’ordre indiqué dans la liste précédente. Il existe quelques exceptions :

  • Les données de routage et les valeurs de chaîne de requête sont utilisées uniquement pour les types simples.
  • Les fichiers chargés sont liés uniquement aux types cibles qui implémentent IFormFile ou IEnumerable<IFormFile>.
  • Si la source par défaut n’est pas correcte, utilisez l’un des attributs suivants pour spécifier la source :

  • [FromQuery] - Obtient les valeurs de la chaîne de requête.
  • [FromRoute] - Obtient des valeurs à partir des données de routage.
  • [FromForm] - Obtient les valeurs des champs de formulaire publiés.
  • [FromBody] - Obtient les valeurs du corps de la requête.
  • [FromHeader] - Obtient les valeurs des en-têtes HTTP.
  • Ces attributs :

  • Sont ajoutés aux propriétés du modèle individuellement et non à la classe de modèle, comme dans l’exemple suivant :

    public class Instructor public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ...
  • Acceptent éventuellement une valeur de nom de modèle dans le constructeur. Cette option est fournie au cas où le nom de propriété ne correspondrait pas à la valeur de la requête. Par exemple, la valeur de la requête peut être un en-tête avec un trait d’union dans son nom, comme dans l’exemple suivant :

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)

    Attribut [FromBody]

    Appliquez l’attribut [FromBody] à un paramètre pour remplir ses propriétés à partir du corps d’une requête HTTP. Le runtime ASP.NET Core délègue la responsabilité de la lecture du corps à un formateur d’entrée. Les formateurs d’entrée sont décrits plus loin dans cet article.

    Lorsque [FromBody] est appliqué à un paramètre de type complexe, tous les attributs de source de liaison appliqués à ses propriétés sont ignorés. Par exemple, l’action suivante Create spécifie que son pet paramètre est rempli à partir du corps :

    public ActionResult<Pet> Create([FromBody] Pet pet)

    La Pet classe spécifie que sa Breed propriété est remplie à partir d’un paramètre de chaîne de requête :

    public class Pet public string Name { get; set; } = null!; [FromQuery] // Attribute is ignored. public string Breed { get; set; } = null!;

    Dans l’exemple précédent :

  • L’attribut [FromQuery] est ignoré.
  • La Breed propriété n’est pas remplie à partir d’un paramètre de chaîne de requête.
  • Les formateurs d’entrée lisent uniquement le corps et ne comprennent pas les attributs de la source de liaison. Si une valeur appropriée est trouvée dans le corps, cette valeur est utilisée pour remplir la Breed propriété.

    N’appliquez pas [FromBody] à plus d’un paramètre par méthode d’action. Une fois le flux de requête lu par un formateur d’entrée, il n’est plus disponible pour être lu à nouveau pour la liaison d’autres [FromBody] paramètres.

    Sources supplémentaires

    Les données sources sont fournies au système de liaison de modèle par les fournisseurs de valeurs. Vous pouvez écrire et inscrire des fournisseurs de valeurs personnalisés qui obtiennent des données de liaison de modèle à partir d’autres sources. Par exemple, vous souhaiterez peut-être obtenir des données provenant de l’état de session ou de cookiela session. Pour obtenir des données provenant d’une nouvelle source :

  • Créez une classe qui implémente IValueProvider.
  • Créez une classe qui implémente IValueProviderFactory.
  • Inscrivez la classe de fabrique dans Program.cs.
  • L’exemple inclut un fournisseur de valeurs et un exemple de fabrique qui obtient des valeurs de cookies. Inscrire des fabriques de fournisseurs de valeurs personnalisées dans Program.cs:

    builder.Services.AddControllers(options => options.ValueProviderFactories.Add(new CookieValueProviderFactory());

    Le code précédent place le fournisseur de valeur personnalisé après tous les fournisseurs de valeurs intégrés. Pour en faire le premier fournisseur de la liste, appelez Insert(0, new CookieValueProviderFactory()) à la place de Add.

    Aucune source pour une propriété de modèle

    Par défaut, aucune erreur d’état de modèle n’est créée, s’il n’existe aucune valeur de propriété de modèle. La propriété a une valeur null ou une valeur par défaut :

  • Les types simples Nullable ont une valeur null.
  • Les types valeur non Nullable ont la valeur default(T). Par exemple, un paramètre int id a la valeur 0.
  • Pour les types complexes, la liaison de modèle crée une instance à l’aide du constructeur par défaut, sans définir de propriétés.
  • Les tableaux ont la valeur Array.Empty<T>(), sauf les tableaux byte[] qui ont une valeur null.
  • Si l’état du modèle doit être invalidé lorsque rien n’est trouvé dans les champs de formulaire pour une propriété de modèle, utilisez l’attribut [BindRequired] .

    Notez que ce [BindRequired] comportement s’applique à la liaison de modèle à partir des données de formulaire publiées, et non aux JSdonnées ON ou XML dans un corps de requête. Les données du corps de requête sont prises en charge par les formateurs d’entrée.

    Erreurs de conversion de type

    Si une source est localisée mais qu’elle ne peut pas être convertie vers le type cible, l’état du modèle est marqué comme étant non valide. Le paramètre ou la propriété cible a une valeur null ou une valeur par défaut, comme indiqué dans la section précédente.

    Dans un contrôleur d’API ayant l’attribut [ApiController], un état de modèle non valide entraîne une réponse HTTP 400 automatique.

    Dans une Razor page, réafficher la page avec un message d’erreur :

    public IActionResult OnPost() if (!ModelState.IsValid) return Page(); // ... return RedirectToPage("./Index");

    Lorsque la page est réaffichée par le code précédent, l’entrée non valide n’est pas affichée dans le champ de formulaire. En effet, la propriété de modèle à une valeur null ou une valeur par défaut. L’entrée non valide apparaît dans un message d’erreur. Si vous souhaitez réafficher les données incorrectes dans le champ de formulaire, envisagez de faire de la propriété de modèle une chaîne et d’effectuer la conversion des données manuellement.

    La même stratégie est recommandée si vous ne souhaitez pas que les erreurs de conversion de type entraînent des erreurs d’état de modèle. Dans ce cas, transformez la propriété de modèle en chaîne.

    Types simples

    Les types simples que le lieur de modèle peut convertir en chaînes sources sont les suivants :

  • Booléen
  • Byte, SByte
  • DateTime
  • DateTimeOffset
  • Décimal
  • Double
  • Int16, Int32, Int64
  • Unique
  • TimeSpan
  • UInt16, UInt32, UInt64
  • Version
  • Types complexes

    Un type complexe doit avoir un constructeur public par défaut et des propriétés publiques accessibles en écriture à lier. Quand la liaison de modèle se produit, la classe est instanciée à l’aide du constructeur public par défaut.

    Pour chaque propriété du type complexe, la liaison de modèle recherche dans les sources le modèle de nomprefix.property_name. Si rien n’est trouvé, elle recherche uniquement nom_propriété sans le préfixe. La décision d’utiliser le préfixe n’est pas prise par propriété. Par exemple, avec une requête contenant ?Instructor.Id=100&Name=foo, liée à la méthode OnGet(Instructor instructor), l’objet résultant de type Instructor contient :

  • Id défini sur 100.
  • Name défini sur null. La liaison de modèle s’attend Instructor.Name car Instructor.Id a été utilisé dans le paramètre de requête précédent.
  • Dans le cas d’une liaison à un paramètre, le préfixe représente le nom du paramètre. Dans le cas d’une liaison à une propriété publique PageModel, le préfixe représente le nom de la propriété publique. Certains attributs ont une propriété Prefix qui vous permet de remplacer l’utilisation par défaut du nom de paramètre ou de propriété.

    Par exemple, supposons que le type complexe corresponde à la classe Instructor suivante :

    public class Instructor
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
    

    Préfixe = nom de paramètre

    Si le modèle à lier est un paramètre nommé instructorToUpdate :

    public IActionResult OnPost(int? id, Instructor instructorToUpdate)
    

    La liaison de modèle commence par rechercher dans les sources la clé instructorToUpdate.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Préfixe = nom de propriété

    Si le modèle à lier est une propriété nommée Instructor du contrôleur ou de la classe PageModel :

    [BindProperty]
    public Instructor Instructor { get; set; }
    

    La liaison de modèle commence par rechercher dans les sources la clé Instructor.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Préfixe personnalisé

    Si le modèle à lier est un paramètre nommé instructorToUpdate et si un attribut Bind spécifie Instructor en tant que préfixe :

    public IActionResult OnPost(
        int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
    

    La liaison de modèle commence par rechercher dans les sources la clé Instructor.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Attributs des cibles de type complexe

    Plusieurs attributs intégrés sont disponibles pour contrôler la liaison de modèle des types complexes :

  • [Bind]
  • [BindRequired]
  • [BindNever]
  • Avertissement

    Ces attributs affectent la liaison de modèle quand les données de formulaire postées représentent la source des valeurs. Elles n’affectent pas les formateurs d’entrée, qui traitent les corps de requête ON et XML publiés JS. Les formateurs d’entrée sont décrits plus loin dans cet article.

    Attribut [Bind]

    Il peut être appliqué à une classe ou à un paramètre de méthode. Il spécifie les propriétés d’un modèle à inclure dans la liaison de modèle. [Bind] n’affecte pas les formateurs d’entrée.

    Dans l’exemple suivant, seules les propriétés spécifiées du modèle Instructor sont liées quand une méthode de gestionnaire ou une méthode d’action est appelée :

    [Bind("LastName,FirstMidName,HireDate")]
    public class Instructor
    

    Dans l’exemple suivant, seules les propriétés spécifiées du modèle Instructor sont liées quand la méthode OnPost est appelée :

    [HttpPost]
    public IActionResult OnPost(
        [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
    

    Vous pouvez utiliser l’attribut [Bind] pour éviter le surpostage dans les scénarios de création. Il ne fonctionne pas bien dans les scénarios de modification, car les propriétés exclues ont une valeur null ou une valeur par défaut au lieu de rester inchangées. Pour empêcher le surpostage, il est recommandé d’utiliser des modèles de vues à la place de l’attribut [Bind]. Pour plus d’informations, consultez Remarque sur la sécurité concernant le surpostage.

    Attribut [ModelBinder]

    ModelBinderAttribute peut être appliqué à des types, des propriétés ou des paramètres. Il permet de spécifier le type de classeur de modèles utilisé pour lier l’instance ou le type spécifique. Par exemple :

    [HttpPost]
    public IActionResult OnPost(
        [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
    

    L’attribut [ModelBinder] peut également être utilisé pour modifier le nom d’une propriété ou d’un paramètre lorsqu’elle est liée au modèle :

    public class Instructor
        [ModelBinder(Name = "instructor_id")]
        public string Id { get; set; }
        // ...
    

    Attribut [BindRequired]

    Il oblige la liaison de modèle à ajouter une erreur d’état de modèle si la liaison est impossible pour la propriété d’un modèle. Voici un exemple :

    public class InstructorBindRequired // ... [BindRequired] public DateTime HireDate { get; set; }

    Consultez également la discussion sur l’attribut [Required] dans Validation de modèle.

    Attribut [BindNever]

    Peut être appliqué à une propriété ou à un type. Il empêche la liaison de modèle de définir la propriété d’un modèle. Lorsqu’il est appliqué à un type, le système de liaison de modèle exclut toutes les propriétés définies par le type. Voici un exemple :

    public class InstructorBindNever [BindNever] public int Id { get; set; } // ...

    Collections

    Pour les cibles qui sont des collections de types simples, la liaison de modèle recherche les correspondances avec nom_paramètre ou nom_propriété. Si aucune correspondance n’est localisée, elle recherche l’un des formats pris en charge sans le préfixe. Par exemple :

  • Supposons que le paramètre à lier soit un tableau nommé selectedCourses :

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Les données de formulaire ou de chaîne de requête peuvent avoir l’un des formats suivants :

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Évitez de lier un paramètre ou une propriété nommée index ou Index s’il est adjacent à une valeur de collection. La liaison de modèle tente d’utiliser index comme index pour la collection, ce qui peut entraîner une liaison incorrecte. Par exemple, considérez l’action suivante :

    public IActionResult Post(string index, List<Product> products)
    

    Dans le code précédent, le paramètre de chaîne de index requête est lié au paramètre de méthode index et est également utilisé pour lier la collection product. Le fait de renommer le index paramètre ou d’utiliser un attribut de liaison de modèle pour configurer la liaison évite ce problème :

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Le format suivant est pris en charge uniquement dans les données de formulaire :

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pour tous les exemples de formats précédents, la liaison de modèle passe un tableau de deux éléments au paramètre selectedCourses :

  • selectedCourses[0]=1050
  • selectedCourses[1]=2000
  • Les formats de données qui utilisent des nombres en indice (... [0] ... [1] ...) doivent être impérativement numérotés de manière séquentielle à partir de zéro. S’il existe des vides dans la numérotation en indice, tous les éléments suivants sont ignorés. Par exemple, si les indices sont 0 et 2 au lieu de 0 et 1, le second élément est ignoré.

    Dictionnaires

    Pour les cibles Dictionary, la liaison de modèle recherche les correspondances avec nom_paramètre ou nom_propriété. Si aucune correspondance n’est localisée, elle recherche l’un des formats pris en charge sans le préfixe. Par exemple :

  • Supposons que le paramètre cible soit un Dictionary<int, string> nommé selectedCourses :

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Les données de chaîne de requête ou de formulaire posté peuvent ressembler à l’un des exemples suivants :

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • Pour tous les exemples de formats précédents, la liaison de modèle passe un dictionnaire de deux éléments au paramètre selectedCourses :

  • selectedCourses["1050"]="Chemistry"
  • selectedCourses["2000"]="Economics"
  • Liaison de constructeur et types d’enregistrements

    La liaison de modèle nécessite que les types complexes aient un constructeur sans paramètre. Les System.Text.Json formateurs d’entrée basés sur et Newtonsoft.Json prennent en charge la désérialisation des classes qui n’ont pas de constructeur sans paramètre.

    Les types d’enregistrements sont un excellent moyen de représenter succinctement des données sur le réseau. ASP.NET Core prend en charge la liaison de modèle et la validation des types d’enregistrements avec un seul constructeur :

    public record Person(
        [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);
    public class PersonController
        public IActionResult Index() => View();
        [HttpPost]
        public IActionResult Index(Person person)
            // ...
    

    Person/Index.cshtml:

    @model Person
    Name: <input asp-for="Name" />
    Age: <input asp-for="Age" />
    

    Lors de la validation des types d’enregistrements, le runtime recherche les métadonnées de liaison et de validation spécifiquement sur les paramètres plutôt que sur les propriétés.

    L’infrastructure permet la liaison aux types d’enregistrements et la validation de ces types :

    public record Person([Required] string Name, [Range(0, 100)] int Age);
    

    Pour que le précédent fonctionne, le type doit :

  • Être un type d’enregistrement.
  • Avoir exactement un constructeur public.
  • Contiennent des paramètres qui ont une propriété avec le même nom et le même type. Les noms ne doivent pas différer selon la casse.
  • POCOs sans constructeurs sans paramètre

    Les POCOs qui n’ont pas de constructeurs sans paramètre ne peuvent pas être liés.

    Le code suivant génère une exception indiquant que le type doit avoir un constructeur sans paramètre :

    public class Person(string Name)
    public record Person([Required] string Name, [Range(0, 100)] int Age)
        public Person(string Name) : this (Name, 0);
    

    Types d’enregistrements avec des constructeurs créés manuellement

    Types d’enregistrements avec des constructeurs créés manuellement qui ressemblent au travail des constructeurs principaux

    public record Person
        public Person([Required] string Name, [Range(0, 100)] int Age)
            => (this.Name, this.Age) = (Name, Age);
        public string Name { get; set; }
        public int Age { get; set; }
    

    Types d’enregistrements, métadonnées de validation et de liaison

    Pour les types d’enregistrements, les métadonnées de validation et de liaison sur les paramètres sont utilisées. Toutes les métadonnées sur les propriétés sont ignorées

    public record Person (string Name, int Age)
       [BindProperty(Name = "SomeName")] // This does not get used
       [Required] // This does not get used
       public string Name { get; init; }
    

    Validation et métadonnées

    La validation utilise des métadonnées sur le paramètre, mais utilise la propriété pour lire la valeur. Dans le cas ordinaire avec les constructeurs principaux, les deux seraient identiques. Toutefois, il existe des moyens de la vaincre :

    public record Person([Required] string Name)
        private readonly string _name;
        // The following property is never null.
        // However this object could have been constructed as "new Person(null)".
        public string Name { get; init => _name = value ?? string.Empty; }
    

    TryUpdateModel ne met pas à jour les paramètres sur un type d’enregistrement

    public record Person(string Name)
        public int Age { get; set; }
    var person = new Person("initial-name");
    TryUpdateModel(person, ...);
    

    Dans ce cas, MVC ne tente pas de lier Name à nouveau. Toutefois, Age est autorisé à être mis à jour

    Comportement de globalisation des données de routage et des chaînes de requête de liaison de modèle

    Le ASP.NET Core fournisseur de valeur de routage et le fournisseur de valeur de chaîne de requête :

  • Traitez les valeurs comme une culture invariante.
  • Attendez-vous à ce que les URL soient indifférentes à la culture.
  • En revanche, les valeurs provenant de données de formulaire subissent une conversion sensible à la culture. Cela est conçu pour que les URL soient partageables entre les paramètres régionaux.

    Pour que le ASP.NET Core route le fournisseur de valeur et le fournisseur de valeurs de chaîne de requête subissent une conversion sensible à la culture :

  • Héritent de IValueProviderFactory
  • Copiez le code de QueryStringValueProviderFactory ou RouteValueValueProviderFactory
  • Remplacez la valeur de culture passée au constructeur du fournisseur de valeurs par CultureInfo.CurrentCulture
  • Remplacez la fabrique de fournisseur de valeurs par défaut dans les options MVC par votre nouvelle :
  • public class CultureQueryStringValueProviderFactory : IValueProviderFactory public Task CreateValueProviderAsync(ValueProviderFactoryContext context) _ = context ?? throw new ArgumentNullException(nameof(context)); var query = context.ActionContext.HttpContext.Request.Query; if (query?.Count > 0) context.ValueProviders.Add( new QueryStringValueProvider( BindingSource.Query, query, CultureInfo.CurrentCulture)); return Task.CompletedTask; builder.Services.AddControllers(options => var index = options.ValueProviderFactories.IndexOf( options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>() .Single()); options.ValueProviderFactories[index] = new CultureQueryStringValueProviderFactory();

    Types de données spéciaux

    Certains types de données spéciaux peuvent être pris en charge par la liaison de modèle.

    IFormFile et IFormFileCollection

    Fichier chargé inclus dans la requête HTTP. IEnumerable<IFormFile> est également pris en charge pour plusieurs fichiers.

    CancellationToken

    Les actions peuvent éventuellement lier un CancellationToken en tant que paramètre. Cette liaison indique RequestAborted quand la connexion sous-jacente à la requête HTTP est abandonnée. Les actions peuvent utiliser ce paramètre pour annuler les opérations asynchrones de longue durée exécutées dans le cadre des actions du contrôleur.

    FormCollection

    Permet de récupérer toutes les valeurs des données de formulaire posté.

    Formateurs d’entrée

    Les données du corps de la requête peuvent être dans JSON, XML ou dans un autre format. Pour analyser ces données, la liaison de modèle utilise un formateur d’entrée configuré pour prendre en charge un type de contenu particulier. Par défaut, ASP.NET Core inclut des JSformateurs d’entrée basés sur ON pour la gestion des JSdonnées ON. Vous pouvez ajouter d’autres formateurs pour d’autres types de contenu.

    ASP.NET Core sélectionne les formateurs d’entrée en fonction de l’attribut Consumes. Si aucun attribut n’est présent, il utilise l’en-tête Content-Type.

    Pour utiliser les formateurs d’entrée XML intégrés :

  • Dans Program.cs, appelez AddXmlSerializerFormatters ou AddXmlDataContractSerializerFormatters.

    builder.Services.AddControllers() .AddXmlSerializerFormatters();
  • Appliquez l’attribut Consumes aux classes de contrôleur ou aux méthodes d’action devant contenir des données XML dans le corps de la requête.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    Pour plus d’informations, consultez Introduction à la sérialisation XML.

    Personnaliser la liaison de modèle avec des formateurs d’entrée

    Un formateur d’entrée assume l’entière responsabilité de la lecture des données du corps de la demande. Pour personnaliser ce processus, configurez les API utilisées par le formateur d’entrée. Cette section explique comment personnaliser le System.Text.Jsonformateur d’entrée basé sur pour comprendre un type personnalisé nommé ObjectId.

    Considérez le modèle suivant, qui contient une propriété personnalisée ObjectId nommée Id:

    public class InstructorObjectId [Required] public ObjectId ObjectId { get; set; } = null!;

    Pour personnaliser le processus de liaison de modèle lors de l’utilisation de System.Text.Json, créez une classe dérivée de JsonConverter<T>:

    internal class ObjectIdConverter : JsonConverter<ObjectId> public override ObjectId Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => new(JsonSerializer.Deserialize<int>(ref reader, options)); public override void Write( Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options) => writer.WriteNumberValue(value.Id);

    Pour utiliser un convertisseur personnalisé, appliquez l’attribut JsonConverterAttribute au type . Dans l’exemple suivant, le ObjectId type est configuré avec ObjectIdConverter comme convertisseur personnalisé :

    [JsonConverter(typeof(ObjectIdConverter))] public record ObjectId(int Id);

    Pour plus d’informations, consultez Comment écrire des convertisseurs personnalisés.

    Exclure les types spécifiés de la liaison de modèle

    Le comportement des systèmes de liaison et de validation de modèle est piloté par ModelMetadata. Vous pouvez personnaliser ModelMetadata en ajoutant un fournisseur de détails à MvcOptions.ModelMetadataDetailsProviders. Des fournisseurs de détails intégrés sont disponibles pour désactiver la liaison de modèle ou la validation des types spécifiés.

    Pour désactiver la liaison de modèle sur tous les modèles d’un type spécifique, ajoutez ExcludeBindingMetadataProvider dans Program.cs. Par exemple, pour désactiver la liaison de modèle sur tous les modèles de type System.Version :

    builder.Services.AddRazorPages() .AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(Guid)));

    Pour désactiver la validation des propriétés d’un type spécifique, ajoutez SuppressChildValidationMetadataProvider dans Program.cs. Par exemple, pour désactiver la validation sur les propriétés de type System.Guid :

    builder.Services.AddRazorPages() .AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(Guid)));

    Lieurs de modèles personnalisés

    Vous pouvez étendre la liaison de modèle en écrivant un lieur de modèle personnalisé et en utilisant l’attribut [ModelBinder] afin de le sélectionner pour une cible donnée. Découvrez plus d’informations sur la liaison de modèle personnalisée.

    Liaison de modèle manuelle

    Vous pouvez appeler la liaison de modèle manuellement à l’aide de la méthode TryUpdateModelAsync. La méthode est définie sur les classes ControllerBase et PageModel. Les surcharges de méthode vous permettent de spécifier le préfixe et le fournisseur de valeurs à utiliser. La méthode retourne false en cas d’échec de la liaison de modèle. Voici un exemple :

    if (await TryUpdateModelAsync( newInstructor, "Instructor", x => x.Name, x => x.HireDate!)) _instructorStore.Add(newInstructor); return RedirectToPage("./Index"); return Page();

    TryUpdateModelAsync utilise des fournisseurs de valeurs pour obtenir des données à partir du corps du formulaire, de la chaîne de requête et des données de routage. TryUpdateModelAsync est généralement :

  • Utilisé avec les Razor applications Pages et MVC à l’aide de contrôleurs et de vues pour empêcher la publication excessive.
  • Non utilisé avec une API web, sauf s’il est consommé à partir de données de formulaire, de chaînes de requête et de données de routage. Les points de terminaison d’API web qui utilisent JSON utilisent des formateurs d’entrée pour désérialiser le corps de la requête en un objet.
  • Pour plus d’informations, consultez TryUpdateModelAsync.

    Attribut [FromServices]

    Le nom de cet attribut suit le modèle des attributs de liaison de modèle qui spécifient une source de données. Toutefois, il ne permet pas de lier les données d’un fournisseur de valeurs. Il obtient une instance d’un type à partir du conteneur d’injection de dépendances. Son objectif est de fournir une alternative à l’injection de constructeurs quand vous avez besoin d’un service uniquement si une méthode particulière est appelée.

    Si une instance du type n’est pas inscrite dans le conteneur d’injection de dépendances, l’application lève une exception lors de la tentative de liaison du paramètre. Pour rendre le paramètre facultatif, utilisez l’une des approches suivantes :

  • Rendez le paramètre nullable.
  • Définissez une valeur par défaut pour le paramètre.
  • Pour les paramètres nullables, vérifiez que le paramètre n’est pas null avant d’y accéder.

    Ressources supplémentaires

  • Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
  • Validation de modèle dans ASP.NET Core MVC
  • Liaison de données personnalisée dans ASP.NET Core
  • Cet article explique ce qu’est la liaison de modèle, comment elle fonctionne et comment personnaliser son comportement.

    Affichez ou téléchargez un exemple de code (procédure de téléchargement).

    Description de la liaison de modèle

    Les contrôleurs et Razor les pages fonctionnent avec des données provenant de requêtes HTTP. Par exemple, les données de routage peuvent fournir une clé d’enregistrement, et les champs de formulaire posté peuvent fournir des valeurs pour les propriétés du modèle. L’écriture du code permettant de récupérer chacune de ces valeurs et de les convertir en types .NET à partir de chaînes est fastidieuse et source d’erreurs. La liaison de modèle automatise ce processus. Le système de liaison de modèle :

  • Récupère les données de diverses sources telles que les données de routage, les champs de formulaire et les chaînes de requête
  • Fournit les données aux contrôleurs et Razor aux pages dans les paramètres de méthode et les propriétés publiques.
  • Convertit les données de chaîne en types .NET
  • Met à jour les propriétés des types complexes
  • Exemple

    Supposons que vous ayez la méthode d’action suivante :

    [HttpGet("{id}")] public ActionResult<Pet> GetById(int id, bool dogsOnly)

    Et que l’application reçoive une requête avec l’URL suivante :

    http://contoso.com/api/pets/2?DogsOnly=true
    

    La liaison de modèle passe par les étapes suivantes une fois que le système de routage a sélectionné la méthode d’action :

  • Elle recherche le premier paramètre de GetById, un entier nommé id.
  • Elle parcourt les sources disponibles dans la requête HTTP et trouve id = « 2 » dans les données de routage.
  • Elle convertit la chaîne « 2 » en entier 2.
  • Elle recherche le paramètre suivant de GetById, un booléen nommé dogsOnly.
  • Elle parcourt les sources et trouve « DogsOnly=true » dans la chaîne de requête. La mise en correspondance des noms ne respecte pas la casse.
  • Elle convertit la chaîne « true » en booléen true.
  • Le framework appelle ensuite la méthode GetById, en passant 2 pour le paramètre id, et true pour le paramètre dogsOnly.

    Dans l’exemple précédent, les cibles de liaison de modèle sont des paramètres de méthode qui sont des types simples. Les cibles peuvent être également les propriétés d’un type complexe. Une fois chaque propriété correctement liée, la validation du modèle est effectuée pour la propriété concernée. L’enregistrement des données liées au modèle (ainsi que des erreurs de liaison ou de validation) est stocké dans ControllerBase.ModelState ou PageModel.ModelState. Pour savoir si ce processus a abouti, l’application vérifie l’indicateur ModelState.IsValid.

    Targets

    La liaison de modèle tente de trouver des valeurs pour les genres de cible suivants :

  • Paramètres de la méthode d’action de contrôleur vers laquelle une requête est routée.
  • Paramètres de la Razor méthode de gestionnaire Pages vers laquelle une requête est acheminée.
  • Propriétés publiques d’un contrôleur ou d’une classe PageModel, si elles sont spécifiées par des attributs.
  • Attribut [BindProperty]

    Peut être appliqué à une propriété publique d’un contrôleur ou à une classe PageModel pour que la liaison de modèle cible cette propriété :

    public class EditModel : InstructorsPageModel [BindProperty] public Instructor Instructor { get; set; }

    Attribut [BindProperties]

    Disponible avec ASP.NET Core 2.1 et les versions ultérieures. Peut être appliqué à un contrôleur ou à une classe PageModel pour indiquer à la liaison de modèle de cibler toutes les propriétés publiques de la classe :

    [BindProperties(SupportsGet = true)] public class CreateModel : InstructorsPageModel public Instructor Instructor { get; set; }

    Liaison de modèle pour les requêtes HTTP GET

    Par défaut, les propriétés ne sont pas liées pour les requêtes HTTP GET. En règle générale, le paramètre ID d’un enregistrement est tout ce dont vous avez besoin pour une requête GET. L’ID d’enregistrement est utilisé pour rechercher l’élément dans la base de données. Il n’est donc pas nécessaire de lier une propriété qui contient une instance du modèle. Pour les scénarios dans lesquels vous souhaitez que les propriétés soient liées aux données provenant de requêtes GET, affectez à la propriété SupportsGet la valeur true :

    [BindProperty(Name = "ai_user", SupportsGet = true)] public string ApplicationInsightsCookie { get; set; }

    Sources

    Par défaut, la liaison de modèle obtient des données sous la forme de paires clé-valeur à partir des sources suivantes dans une requête HTTP :

  • Champs de formulaire
  • Corps de la requête (pour les contrôleurs ayant l’attribut [ApiController].)
  • Données de routage
  • Paramètres de chaîne de requête
  • Fichiers chargés
  • Pour chaque paramètre ou propriété cible, les sources sont analysées dans l’ordre indiqué dans la liste précédente. Il existe quelques exceptions :

  • Les données de routage et les valeurs de chaîne de requête sont utilisées uniquement pour les types simples.
  • Les fichiers chargés sont liés uniquement aux types cibles qui implémentent IFormFile ou IEnumerable<IFormFile>.
  • Si la source par défaut n’est pas correcte, utilisez l’un des attributs suivants pour spécifier la source :

  • [FromQuery] - Obtient les valeurs de la chaîne de requête.
  • [FromRoute] - Obtient des valeurs à partir des données de routage.
  • [FromForm] - Obtient les valeurs des champs de formulaire publiés.
  • [FromBody] - Obtient les valeurs du corps de la requête.
  • [FromHeader] - Obtient des valeurs à partir d’en-têtes HTTP.
  • Ces attributs :

  • Sont ajoutés aux propriétés du modèle individuellement (et non à la classe de modèle), comme dans l’exemple suivant :

    public class Instructor public int ID { get; set; } [FromQuery(Name = "Note")] public string NoteFromQueryString { get; set; }
  • Acceptent éventuellement une valeur de nom de modèle dans le constructeur. Cette option est fournie au cas où le nom de propriété ne correspondrait pas à la valeur de la requête. Par exemple, la valeur de la requête peut être un en-tête avec un trait d’union dans son nom, comme dans l’exemple suivant :

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)

    Attribut [FromBody]

    Appliquez l’attribut [FromBody] à un paramètre pour remplir ses propriétés à partir du corps d’une requête HTTP. Le runtime ASP.NET Core délègue la responsabilité de lire le corps à un formateur d’entrée. Les formateurs d’entrée sont décrits plus loin dans cet article.

    Quand [FromBody] est appliqué à un paramètre de type complexe, tous les attributs de source de liaison appliqués à ses propriétés sont ignorés. Par exemple, l’action suivante Create spécifie que son pet paramètre est renseigné à partir du corps :

    public ActionResult<Pet> Create([FromBody] Pet pet)
    

    La Pet classe spécifie que sa Breed propriété est remplie à partir d’un paramètre de chaîne de requête :

    public class Pet
        public string Name { get; set; }
        [FromQuery] // Attribute is ignored.
        public string Breed { get; set; }
    

    Dans l’exemple précédent :

  • L’attribut [FromQuery] est ignoré.
  • La Breed propriété n’est pas renseignée à partir d’un paramètre de chaîne de requête.
  • Les formateurs d’entrée lisent uniquement le corps et ne comprennent pas les attributs sources de liaison. Si une valeur appropriée est trouvée dans le corps, cette valeur est utilisée pour remplir la Breed propriété.

    N’appliquez pas [FromBody] à plus d’un paramètre par méthode d’action. Une fois que le flux de requête est lu par un formateur d’entrée, il n’est plus disponible pour être lu à nouveau pour lier d’autres [FromBody] paramètres.

    Sources supplémentaires

    Les données sources sont fournies au système de liaison de modèle par les fournisseurs de valeurs. Vous pouvez écrire et inscrire des fournisseurs de valeurs personnalisés qui obtiennent des données de liaison de modèle à partir d’autres sources. Par exemple, vous pouvez souhaiter des données à partir de l’état s cookieou de session. Pour obtenir des données provenant d’une nouvelle source :

  • Créez une classe qui implémente IValueProvider.
  • Créez une classe qui implémente IValueProviderFactory.
  • Inscrivez la classe de fabrique dans Startup.ConfigureServices.
  • L’exemple d’application inclut un fournisseur de valeurs et un exemple de fabrique qui obtient des valeurs à partir de cookies. Voici le code d’inscription dans Startup.ConfigureServices :

    services.AddRazorPages() .AddMvcOptions(options => options.ValueProviderFactories.Add(new CookieValueProviderFactory()); options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(System.Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(System.Guid))); .AddXmlSerializerFormatters();

    Le code affiché place le fournisseur de valeurs personnalisé après tous les fournisseurs de valeurs intégrés. Pour en faire le premier fournisseur de la liste, appelez Insert(0, new CookieValueProviderFactory()) à la place de Add.

    Aucune source pour une propriété de modèle

    Par défaut, aucune erreur d’état de modèle n’est créée, s’il n’existe aucune valeur de propriété de modèle. La propriété a une valeur null ou une valeur par défaut :

  • Les types simples Nullable ont une valeur null.
  • Les types valeur non Nullable ont la valeur default(T). Par exemple, un paramètre int id a la valeur 0.
  • Pour les types complexes, la liaison de modèle crée une instance à l’aide du constructeur par défaut, sans définir de propriétés.
  • Les tableaux ont la valeur Array.Empty<T>(), sauf les tableaux byte[] qui ont une valeur null.
  • Si l’état du modèle doit être invalidé lorsque rien n’est trouvé dans les champs de formulaire d’une propriété de modèle, utilisez l’attribut [BindRequired] .

    Notez que ce [BindRequired] comportement s’applique à la liaison de modèle à partir de données de formulaire publiées, et non aux JSdonnées ON ou XML dans un corps de requête. Les données du corps de requête sont prises en charge par les formateurs d’entrée.

    Erreurs de conversion de type

    Si une source est localisée mais qu’elle ne peut pas être convertie vers le type cible, l’état du modèle est marqué comme étant non valide. Le paramètre ou la propriété cible a une valeur null ou une valeur par défaut, comme indiqué dans la section précédente.

    Dans un contrôleur d’API ayant l’attribut [ApiController], un état de modèle non valide entraîne une réponse HTTP 400 automatique.

    Dans une Razor page, réafficher la page avec un message d’erreur :

    public IActionResult OnPost() if (!ModelState.IsValid) return Page(); _instructorsInMemoryStore.Add(Instructor); return RedirectToPage("./Index");

    La validation côté client intercepte la plupart des données incorrectes qui seraient autrement soumises à un Razor formulaire Pages. Cette validation rend difficile le déclenchement du code en surbrillance indiqué plus haut. L’exemple d’application comprend un bouton Submit with Invalid Date (Envoyer avec une date non valide), qui place les données incorrectes dans le champ Hire Date (Date d’embauche) et envoie le formulaire. Ce bouton montre comment fonctionne le code permettant de réafficher la page quand des erreurs de conversion de données se produisent.

    Quand la page est réaffichée par le code précédent, l’entrée non valide n’est pas visible dans le champ de formulaire. En effet, la propriété de modèle à une valeur null ou une valeur par défaut. L’entrée non valide apparaît dans un message d’erreur. Toutefois, si vous souhaitez réafficher les données incorrectes dans le champ de formulaire, transformez la propriété de modèle en chaîne et procédez à la conversion des données manuellement.

    La même stratégie est recommandée si vous ne souhaitez pas que les erreurs de conversion de type entraînent des erreurs d’état de modèle. Dans ce cas, transformez la propriété de modèle en chaîne.

    Types simples

    Les types simples que le lieur de modèle peut convertir en chaînes sources sont les suivants :

  • Booléen
  • Byte, SByte
  • DateTime
  • DateTimeOffset
  • Décimal
  • Double
  • Int16, Int32, Int64
  • Unique
  • TimeSpan
  • UInt16, UInt32, UInt64
  • Version
  • Types complexes

    Un type complexe doit avoir un constructeur public par défaut et des propriétés publiques accessibles en écriture à lier. Quand la liaison de modèle se produit, la classe est instanciée à l’aide du constructeur public par défaut.

    Pour chaque propriété du type complexe, la liaison de modèle recherche dans les sources le modèle de nom préfixe.nom_propriété. Si rien n’est trouvé, elle recherche uniquement nom_propriété sans le préfixe.

    Dans le cas d’une liaison à un paramètre, le préfixe représente le nom du paramètre. Dans le cas d’une liaison à une propriété publique PageModel, le préfixe représente le nom de la propriété publique. Certains attributs ont une propriété Prefix qui vous permet de remplacer l’utilisation par défaut du nom de paramètre ou de propriété.

    Par exemple, supposons que le type complexe corresponde à la classe Instructor suivante :

    public class Instructor
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
    

    Préfixe = nom de paramètre

    Si le modèle à lier est un paramètre nommé instructorToUpdate :

    public IActionResult OnPost(int? id, Instructor instructorToUpdate)
    

    La liaison de modèle commence par rechercher dans les sources la clé instructorToUpdate.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Préfixe = nom de propriété

    Si le modèle à lier est une propriété nommée Instructor du contrôleur ou de la classe PageModel :

    [BindProperty]
    public Instructor Instructor { get; set; }
    

    La liaison de modèle commence par rechercher dans les sources la clé Instructor.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Préfixe personnalisé

    Si le modèle à lier est un paramètre nommé instructorToUpdate et si un attribut Bind spécifie Instructor en tant que préfixe :

    public IActionResult OnPost(
        int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
    

    La liaison de modèle commence par rechercher dans les sources la clé Instructor.ID. Si elle est introuvable, elle recherche ID sans préfixe.

    Attributs des cibles de type complexe

    Plusieurs attributs intégrés sont disponibles pour contrôler la liaison de modèle des types complexes :

  • [Bind]
  • [BindRequired]
  • [BindNever]
  • Avertissement

    Ces attributs affectent la liaison de modèle quand les données de formulaire postées représentent la source des valeurs. Elles n’affectent pas les formateurs d’entrée, qui traitent les corps de requêtes ON et XML publiés JS. Les formateurs d’entrée sont décrits plus loin dans cet article.

    Attribut [Bind]

    Il peut être appliqué à une classe ou à un paramètre de méthode. Il spécifie les propriétés d’un modèle à inclure dans la liaison de modèle. [Bind]n’affecte pas les formateurs d’entrée.

    Dans l’exemple suivant, seules les propriétés spécifiées du modèle Instructor sont liées quand une méthode de gestionnaire ou une méthode d’action est appelée :

    [Bind("LastName,FirstMidName,HireDate")]
    public class Instructor
    

    Dans l’exemple suivant, seules les propriétés spécifiées du modèle Instructor sont liées quand la méthode OnPost est appelée :

    [HttpPost]
    public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
    

    Vous pouvez utiliser l’attribut [Bind] pour éviter le surpostage dans les scénarios de création. Il ne fonctionne pas bien dans les scénarios de modification, car les propriétés exclues ont une valeur null ou une valeur par défaut au lieu de rester inchangées. Pour empêcher le surpostage, il est recommandé d’utiliser des modèles de vues à la place de l’attribut [Bind]. Pour plus d’informations, consultez Remarque sur la sécurité concernant le surpostage.

    Attribut [ModelBinder]

    ModelBinderAttribute peut être appliqué à des types, des propriétés ou des paramètres. Il permet de spécifier le type de classeur de modèle utilisé pour lier l’instance ou le type spécifique. Par exemple :

    [HttpPost]
    public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
    

    L’attribut [ModelBinder] peut également être utilisé pour modifier le nom d’une propriété ou d’un paramètre lorsqu’il est lié au modèle :

    public class Instructor
        [ModelBinder(Name = "instructor_id")]
        public string Id { get; set; }
        public string Name { get; set; }
    

    Attribut [BindRequired]

    Il s’applique uniquement aux propriétés de modèle, pas aux paramètres de méthode. Il oblige la liaison de modèle à ajouter une erreur d’état de modèle si la liaison est impossible pour la propriété d’un modèle. Voici un exemple :

    public class InstructorWithCollection public int ID { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] [Display(Name = "Hire Date")] [BindRequired] public DateTime HireDate { get; set; }

    Consultez également la discussion sur l’attribut [Required] dans Validation de modèle.

    Attribut [BindNever]

    Il s’applique uniquement aux propriétés de modèle, pas aux paramètres de méthode. Il empêche la liaison de modèle de définir la propriété d’un modèle. Voici un exemple :

    public class InstructorWithDictionary [BindNever] public int ID { get; set; }

    Collections

    Pour les cibles qui sont des collections de types simples, la liaison de modèle recherche les correspondances avec nom_paramètre ou nom_propriété. Si aucune correspondance n’est localisée, elle recherche l’un des formats pris en charge sans le préfixe. Par exemple :

  • Supposons que le paramètre à lier soit un tableau nommé selectedCourses :

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Les données de formulaire ou de chaîne de requête peuvent avoir l’un des formats suivants :

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Évitez de lier un paramètre ou une propriété nommée index ou Index s’il est adjacent à une valeur de collection. La liaison de modèle tente d’utiliser index comme index pour la collection, ce qui peut entraîner une liaison incorrecte. Par exemple, considérez l’action suivante :

    public IActionResult Post(string index, List<Product> products)
    

    Dans le code précédent, le paramètre de chaîne de index requête se lie au paramètre de méthode index et est également utilisé pour lier la collection de produits. Le changement de nom du index paramètre ou l’utilisation d’un attribut de liaison de modèle pour configurer la liaison évite ce problème :

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Le format suivant est pris en charge uniquement dans les données de formulaire :

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pour tous les exemples de formats précédents, la liaison de modèle passe un tableau de deux éléments au paramètre selectedCourses :

  • selectedCourses[0]=1050
  • selectedCourses[1]=2000
  • Les formats de données qui utilisent des nombres en indice (... [0] ... [1] ...) doivent être impérativement numérotés de manière séquentielle à partir de zéro. S’il existe des vides dans la numérotation en indice, tous les éléments suivants sont ignorés. Par exemple, si les indices sont 0 et 2 au lieu de 0 et 1, le second élément est ignoré.

    Dictionnaires

    Pour les cibles Dictionary, la liaison de modèle recherche les correspondances avec nom_paramètre ou nom_propriété. Si aucune correspondance n’est localisée, elle recherche l’un des formats pris en charge sans le préfixe. Par exemple :

  • Supposons que le paramètre cible soit un Dictionary<int, string> nommé selectedCourses :

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Les données de chaîne de requête ou de formulaire posté peuvent ressembler à l’un des exemples suivants :

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
  •