Créer des API web avec ASP.NET Core

ASP.NET Core prend en charge la création d’API web à l’aide de contrôleurs ou d’API minimales. Les contrôleurs dans une API web sont des classes qui dérivent de ControllerBase. Les contrôleurs sont activés et supprimés en fonction des requêtes.

Cet article explique comment utiliser des contrôleurs pour gérer les demandes d’API web. Pour plus d’informations sur la création d’API web sans contrôleurs, consultez Tutoriel : créer une API minimale avec ASP.NET Core.

Classe ControllerBase

Une API web basée sur un contrôleur se compose d’une ou de plusieurs classes de contrôleur qui dérivent de ControllerBase. Le modèle de projet d’API web fournit un contrôleur de démarrage :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Les contrôleurs d’API web doivent généralement dériver de ControllerBase plutôt que de Controller. Controller dérive de ControllerBase et ajoute la prise en charge pour les vues ; ainsi, il est destiné à la gestion des pages web, pas des demandes d’API web. Si le même contrôleur doit prendre en charge les vues et les API web, il dérive de Controller.

La classe ControllerBase fournit de nombreuses propriétés et méthodes qui sont utiles pour gérer les requêtes HTTP. Par exemple, CreatedAtAction retourne un code d’état 201 :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Le tableau suivant contient des exemples de méthodes dans ControllerBase.

Méthode Notes
BadRequest Retourne le code d’état 400.
NotFound Retourne le code d’état 404.
PhysicalFile Retourne un fichier.
TryUpdateModelAsync Appelle la liaison de modèle.
TryValidateModel Appelle la validation de modèle.

Pour obtenir la liste complète des méthodes et propriétés disponibles, consultez ControllerBase.

Attributs

L’espace de noms Microsoft.AspNetCore.Mvc fournit des attributs qui peuvent être utilisés pour configurer le comportement des contrôleurs d’API web et des méthodes d’action. L’exemple suivant utilise des attributs pour spécifier le verbe d’action HTTP pris en charge et l’ensemble des codes d’état HTTP connus qui peuvent être retournés :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Voici d’autres exemples d’attributs disponibles.

Attribut Notes
[Route] Spécifie le modèle d’URL pour un contrôleur ou une action.
[Bind] Spécifie le préfixe et les propriétés à inclure pour la liaison de modèle.
[HttpGet] Identifie une action qui prend en charge le verbe d’action HTTP GET.
[Consumes] Spécifie les types de données acceptés par une action.
[Produces] Spécifie les types de données retournés par une action.

Pour obtenir la liste des attributs disponibles, consultez l’espace de noms Microsoft.AspNetCore.Mvc.

Attribut ApiController

L’attribut [ApiController] peut être appliqué à une classe de contrôleur pour activer les comportements rigides spécifiques à l’API suivants :

Attribut sur des contrôleurs spécifiques

L’attribut [ApiController] peut être appliqué à des contrôleurs spécifiques, comme dans l’exemple suivant à partir du modèle de projet :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Attribut sur plusieurs contrôleurs

Une façon d’utiliser l’attribut sur plusieurs contrôleurs consiste à créer une classe de contrôleur de base personnalisée annotée avec l’attribut [ApiController]. L’exemple suivant montre une classe de base personnalisée et un contrôleur qui dérive de celle-ci :

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Attribut sur un assembly

L’attribut [ApiController] peut être appliqué à un assembly. Quand l’attribut [ApiController] est appliqué à un assembly, l’attribut [ApiController] est appliqué à l’ensemble des contrôleurs de l’assembly. Les contrôleurs individuels n’ont aucun moyen de refuser. Appliquez l’attribut de niveau assembly au fichier Program.cs :

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Exigence du routage d’attribut

L’attribut [ApiController] rend nécessaire le routage d’attributs. Par exemple :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Les actions sont inaccessibles via les itinéraires conventionnels définis par UseEndpoints, UseMvc ou UseMvcWithDefaultRoute.

Réponses HTTP 400 automatiques

Grâce à l’attribut [ApiController], les erreurs de validation de modèles déclenchent automatiquement une réponse HTTP 400. Ainsi, le code suivant est inutile dans une méthode d’action :

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC utilise le filtre d’action ModelStateInvalidFilter pour effectuer la vérification précédente.

Réponse BadRequest par défaut

Le type de réponse par défaut pour une réponse HTTP 400 est ValidationProblemDetails. Le corps de réponse suivant est un exemple de type sérialisé :

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Type de ValidationProblemDetails :

  • Fournit un format lisible par l’ordinateur permettant de spécifier les erreurs dans les réponses de l’API web.
  • Conforme à la spécification RFC 7807.

Pour assurer la cohérence des réponses automatiques et personnalisées, appelez la méthode ValidationProblem au lieu de BadRequest. ValidationProblem retourne un objet ValidationProblemDetails, ainsi que la réponse automatique.

Consigner automatiquement 400 réponses

Pour consigner les réponses automatiques 400, définissez la propriété de délégué InvalidModelStateResponseFactory afin d’effectuer un traitement personnalisé. Par défaut, InvalidModelStateResponseFactory utilise ProblemDetailsFactory pour créer une instance ValidationProblemDetails.

L’exemple suivant montre comment récupérer une instance de ILogger<TCategoryName> pour enregistrer des informations sur une réponse automatique 400 :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Désactiver la réponse 400 automatique

Pour désactiver le comportement 400 automatique, définissez la propriété SuppressModelStateInvalidFilter sur true. Ajoutez le code en surbrillance suivant :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inférence de paramètre de source de liaison

Un attribut de source de liaison définit l’emplacement auquel se trouve la valeur d’un paramètre d’action. Les attributs de source de liaison suivants existent :

Attribut Source de liaison
[FromBody] Corps de la demande
[FromForm] Données de formulaire dans le corps de la demande
[FromHeader] En-tête de requête
[FromQuery] Paramètre de la chaîne de requête de la demande
[FromRoute] Données d’itinéraire à partir de la demande actuelle
[FromServices] Service de demande injecté comme paramètre d’action
[AsParameters] Paramètres de la méthode

Avertissement

N’utilisez pas [FromRoute] lorsque les valeurs risquent de contenir %2f (c’est-à-dire /). %2f ne sera pas sans la séquence d’échappement /. Utilisez [FromQuery] si la valeur peut contenir %2f.

Sans l’attribut [ApiController] ou des attributs de source de liaison comme [FromQuery], le runtime ASP.NET Core tente d’utiliser le classeur de modèles objet complexe. Le classeur de modèles objet complexe extrait des données à partir de fournisseurs de valeurs dans un ordre défini.

Dans l’exemple suivant, l’attribut [FromQuery] indique que la valeur du paramètre discontinuedOnly est fournie dans la chaîne de requête de l’URL de demande :

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L’attribut [ApiController] applique des règles d’inférence pour les sources de données par défaut des paramètres d’action. Ces règles vous évitent d’avoir à identifier les sources de liaison manuellement en appliquant des attributs aux paramètres d’action. Les règles d’inférence de source de liaison se comportent comme suit :

  • [FromServices] est déduit pour les paramètres de type complexe enregistrés dans le conteneur DI.
  • [FromBody] est déduit pour les paramètres de type complexe qui ne sont pas inscrits dans le conteneur d’injection de dépendances. Une exception à cette règle d’inférence [FromBody] est tout type complexe intégré ayant une signification spéciale, comme IFormCollection et CancellationToken. Le code de l’inférence de la source de liaison ignore ces types spéciaux.
  • [FromForm] est déduit pour les paramètres d’action de type IFormFile et IFormFileCollection. Il n’est pas déduit pour les types simples ou définis par l’utilisateur.
  • [FromRoute] est déduit pour tout nom de paramètre d’action correspondant à un paramètre dans le modèle d’itinéraire. Quand plus d’un itinéraire correspond à un paramètre d’action, toute valeur d’itinéraire est considérée comme [FromRoute].
  • [FromQuery] est déduit pour tous les autres paramètres d’action.

Notes sur l’inférence FromBody

[FromBody] n’est pas déduit pour les types simples tels que string ou int. Vous devez donc utiliser l’attribut [FromBody] pour les types simples quand cette fonctionnalité est nécessaire.

Quand une action a plusieurs paramètres liés à partir du corps de la demande, une exception est levée. Par exemple, toutes les signatures de méthode d’action suivantes génèrent une exception :

  • [FromBody] est déduit sur les deux paramètres, car ce sont des types complexes.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Attribut [FromBody] sur un paramètre, déduit sur l’autre, car c’est un type complexe.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Attribut [FromBody] sur les deux paramètres.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Remarques sur l’inférence FromServices

La liaison de paramètre lie des paramètres via l’injection de dépendance quand le type est configuré en tant que service. Cela signifie qu’il n’est pas nécessaire d’appliquer explicitement l’attribut [FromServices] à un paramètre. Dans le code suivant, les deux actions retournent l’heure :

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

Dans de rares cas, l’injection de dépendances automatique peut interrompre les applications qui présentent un type dans l’injection de dépendances qui est également accepté dans les méthodes d’action d’un contrôleur d’API. Il n’est pas courant d’avoir un type dans l’injection de dépendances et comme argument dans une action du contrôleur d’API.

Pour désactiver l’inférence[FromServices] pour un paramètre d’action unique, appliquez l’attribut de source de liaison souhaité au paramètre. Appliquez par exemple l’attribut [FromBody] à un paramètre d’action qui doit être lié à partir du corps de la demande.

Pour désactiver l’inférence [FromServices] globalement, définissez DisableImplicitFromServicesParameters sur true :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

Les types sont vérifiés au démarrage de l’application avec IServiceProviderIsService pour déterminer si un argument dans une action de contrôleur d’API provient de l’injection de dépendances ou d’autres sources.

Le mécanisme permettant de déduire la source de liaison des paramètres d’action du contrôleur d’API utilise les règles suivantes :

  • Un BindingInfo.BindingSource spécifié précédemment n’est jamais remplacé.
  • BindingSource.Services est affecté à un paramètre de type complexe, inscrit dans le conteneur d’injection de dépendances.
  • BindingSource.Body est affecté à un paramètre de type complexe, qui n’est pas inscrit dans le conteneur d’injection de dépendances.
  • BindingSource.Path est affecté à un paramètre avec un nom qui apparaît en tant que valeur de routage dans n’importe quel modèle de routage.
  • Tous les autres paramètres sont BindingSource.Query.

Désactiver les règles d’inférence

Pour désactiver l’inférence de la source de liaison, définissez SuppressInferBindingSourcesForParameters sur true :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inférence de demande multipart/form-data

L’attribut [ApiController] applique une règle d’inférence pour les paramètres d’action de type IFormFile et IFormFileCollection. Le type de contenu de la demande multipart/form-data est déduit pour ces types.

Pour désactiver le comportement par défaut, définissez la propriété SuppressConsumesConstraintForFormFileParameters sur true :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Fonctionnalité Détails du problème pour les codes d’état erreur

MVC transforme un résultat d’erreur (résultat avec le code d’état 400 ou supérieur) en résultat avec ProblemDetails. Le type ProblemDetails est basé sur la spécification RFC 7807 pour fournir des détails de l’erreur lisibles par machine dans une réponse HTTP.

Prenons le code suivant dans une action de contrôleur :

if (pet == null)
{
    return NotFound();
}

La méthode NotFound produit un code d’état HTTP 404 avec un corps ProblemDetails. Par exemple :

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Désactiver la réponse ProblemDetails

La création automatique d’un ProblemDetails pour les codes d’état d’erreur est désactivée quand la propriété SuppressMapClientErrors est définie sur true. Ajoutez le code suivant :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Définir les types de contenu de la demande pris en charge avec l’attribut [Consumes]

Par défaut, une action prend en charge tous les types de contenu de la demande disponibles. Par exemple, si une application est configurée pour prendre en charge à la fois les formateurs d’entréeJSON et XML, une action prend en charge plusieurs types de contenu, notamment application/json et application/xml.

L’attribut [Consumes] permet à une action de limiter les types de contenu de la demande pris en charge. Appliquez l’attribut [Consumes] à une action ou à un contrôleur, en spécifiant un ou plusieurs types de contenu :

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Dans le code précédent, l’action CreateProduct spécifie le type de contenu application/xml. Les demandes routées vers cette action doivent spécifier un en-tête Content-Type de application/xml. Les demandes qui ne spécifient pas un en-tête Content-Type de application/xml entraînent une réponse 415 Type de média non pris en charge.

L’attribut [Consumes] permet également à une action d’influencer la sélection en fonction du type de contenu de la demande entrante en appliquant une contrainte de type. Prenons l’exemple suivant :

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Dans le code précédent, ConsumesController est configuré pour gérer les demandes envoyées à l’URL https://localhost:5001/api/Consumes. Les actions du contrôleur PostJson et PostForm gèrent les demandes POST ayant la même URL. Sans l’attribut [Consumes] qui applique une contrainte de type, une exception de correspondance ambiguë est levée.

L’attribut [Consumes] est appliqué aux deux actions. L’action PostJson gère les demandes envoyées avec un en-tête Content-Type de application/json. L’action PostForm gère les demandes envoyées avec un en-tête Content-Type de application/x-www-form-urlencoded.

Ressources supplémentaires

ASP.NET Core prend en charge la création d’API web à l’aide de contrôleurs ou d’API minimales. Les contrôleurs dans une API web sont des classes qui dérivent de ControllerBase. Cet article explique comment utiliser des contrôleurs pour gérer les demandes d’API web. Pour plus d’informations sur la création d’API web sans contrôleurs, consultez Tutoriel : créer une API minimale avec ASP.NET Core.

Classe ControllerBase

Une API web basée sur un contrôleur se compose d’une ou de plusieurs classes de contrôleur qui dérivent de ControllerBase. Le modèle de projet d’API web fournit un contrôleur de démarrage :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Les contrôleurs d’API web doivent généralement dériver de ControllerBase plutôt que de Controller. Controller dérive de ControllerBase et ajoute la prise en charge pour les vues ; ainsi, il est destiné à la gestion des pages web, pas des demandes d’API web. Si le même contrôleur doit prendre en charge les vues et les API web, il dérive de Controller.

La classe ControllerBase fournit de nombreuses propriétés et méthodes qui sont utiles pour gérer les requêtes HTTP. Par exemple, CreatedAtAction retourne un code d’état 201 :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Le tableau suivant contient des exemples de méthodes dans ControllerBase.

Méthode Notes
BadRequest Retourne le code d’état 400.
NotFound Retourne le code d’état 404.
PhysicalFile Retourne un fichier.
TryUpdateModelAsync Appelle la liaison de modèle.
TryValidateModel Appelle la validation de modèle.

Pour obtenir la liste complète des méthodes et propriétés disponibles, consultez ControllerBase.

Attributs

L’espace de noms Microsoft.AspNetCore.Mvc fournit des attributs qui peuvent être utilisés pour configurer le comportement des contrôleurs d’API web et des méthodes d’action. L’exemple suivant utilise des attributs pour spécifier le verbe d’action HTTP pris en charge et l’ensemble des codes d’état HTTP connus qui peuvent être retournés :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Voici d’autres exemples d’attributs disponibles.

Attribut Notes
[Route] Spécifie le modèle d’URL pour un contrôleur ou une action.
[Bind] Spécifie le préfixe et les propriétés à inclure pour la liaison de modèle.
[HttpGet] Identifie une action qui prend en charge le verbe d’action HTTP GET.
[Consumes] Spécifie les types de données acceptés par une action.
[Produces] Spécifie les types de données retournés par une action.

Pour obtenir la liste des attributs disponibles, consultez l’espace de noms Microsoft.AspNetCore.Mvc.

Attribut ApiController

L’attribut [ApiController] peut être appliqué à une classe de contrôleur pour activer les comportements rigides spécifiques à l’API suivants :

Attribut sur des contrôleurs spécifiques

L’attribut [ApiController] peut être appliqué à des contrôleurs spécifiques, comme dans l’exemple suivant à partir du modèle de projet :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Attribut sur plusieurs contrôleurs

Une façon d’utiliser l’attribut sur plusieurs contrôleurs consiste à créer une classe de contrôleur de base personnalisée annotée avec l’attribut [ApiController]. L’exemple suivant montre une classe de base personnalisée et un contrôleur qui dérive de celle-ci :

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Attribut sur un assembly

L’attribut [ApiController] peut être appliqué à un assembly. Quand l’attribut [ApiController] est appliqué à un assembly, l’attribut [ApiController] est appliqué à l’ensemble des contrôleurs de l’assembly. Les contrôleurs individuels n’ont aucun moyen de refuser. Appliquez l’attribut de niveau assembly au fichier Program.cs :

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Exigence du routage d’attribut

L’attribut [ApiController] rend nécessaire le routage d’attributs. Par exemple :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Les actions sont inaccessibles via les itinéraires conventionnels définis par UseEndpoints, UseMvc ou UseMvcWithDefaultRoute.

Réponses HTTP 400 automatiques

Grâce à l’attribut [ApiController], les erreurs de validation de modèles déclenchent automatiquement une réponse HTTP 400. Ainsi, le code suivant est inutile dans une méthode d’action :

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC utilise le filtre d’action ModelStateInvalidFilter pour effectuer la vérification précédente.

Réponse BadRequest par défaut

Le corps de réponse suivant est un exemple de type sérialisé :

{
  "": [
    "A non-empty request body is required."
  ]
}

Le type de réponse par défaut pour une réponse HTTP 400 est ValidationProblemDetails. Le corps de réponse suivant est un exemple de type sérialisé :

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Type de ValidationProblemDetails :

  • Fournit un format lisible par l’ordinateur permettant de spécifier les erreurs dans les réponses de l’API web.
  • Conforme à la spécification RFC 7807.

Pour assurer la cohérence des réponses automatiques et personnalisées, appelez la méthode ValidationProblem au lieu de BadRequest. ValidationProblem retourne un objet ValidationProblemDetails, ainsi que la réponse automatique.

Consigner automatiquement 400 réponses

Pour consigner les réponses automatiques 400, définissez la propriété de délégué InvalidModelStateResponseFactory afin d’effectuer un traitement personnalisé. Par défaut, InvalidModelStateResponseFactory utilise ProblemDetailsFactory pour créer une instance ValidationProblemDetails.

L’exemple suivant montre comment récupérer une instance de ILogger<TCategoryName> pour enregistrer des informations sur une réponse automatique 400 :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Désactiver la réponse 400 automatique

Pour désactiver le comportement 400 automatique, définissez la propriété SuppressModelStateInvalidFilter sur true. Ajoutez le code en surbrillance suivant :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inférence de paramètre de source de liaison

Un attribut de source de liaison définit l’emplacement auquel se trouve la valeur d’un paramètre d’action. Les attributs de source de liaison suivants existent :

Attribut Source de liaison
[FromBody] Corps de la demande
[FromForm] Données de formulaire dans le corps de la demande
[FromHeader] En-tête de requête
[FromQuery] Paramètre de la chaîne de requête de la demande
[FromRoute] Données d’itinéraire à partir de la demande actuelle
[FromServices] Service de demande injecté comme paramètre d’action

Avertissement

N’utilisez pas [FromRoute] lorsque les valeurs risquent de contenir %2f (c’est-à-dire /). %2f ne sera pas sans la séquence d’échappement /. Utilisez [FromQuery] si la valeur peut contenir %2f.

Sans l’attribut [ApiController] ou des attributs de source de liaison comme [FromQuery], le runtime ASP.NET Core tente d’utiliser le classeur de modèles objet complexe. Le classeur de modèles objet complexe extrait des données à partir de fournisseurs de valeurs dans un ordre défini.

Dans l’exemple suivant, l’attribut [FromQuery] indique que la valeur du paramètre discontinuedOnly est fournie dans la chaîne de requête de l’URL de demande :

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L’attribut [ApiController] applique des règles d’inférence pour les sources de données par défaut des paramètres d’action. Ces règles vous évitent d’avoir à identifier les sources de liaison manuellement en appliquant des attributs aux paramètres d’action. Les règles d’inférence de source de liaison se comportent comme suit :

  • [FromBody] est déduit pour les paramètres de type complexe qui ne sont pas inscrits dans le conteneur d’injection de dépendances. Une exception à cette règle d’inférence [FromBody] est tout type complexe intégré ayant une signification spéciale, comme IFormCollection et CancellationToken. Le code de l’inférence de la source de liaison ignore ces types spéciaux.
  • [FromForm] est déduit pour les paramètres d’action de type IFormFile et IFormFileCollection. Il n’est pas déduit pour les types simples ou définis par l’utilisateur.
  • [FromRoute] est déduit pour tout nom de paramètre d’action correspondant à un paramètre dans le modèle d’itinéraire. Quand plus d’un itinéraire correspond à un paramètre d’action, toute valeur d’itinéraire est considérée comme [FromRoute].
  • [FromQuery] est déduit pour tous les autres paramètres d’action.

Notes sur l’inférence FromBody

[FromBody] n’est pas déduit pour les types simples tels que string ou int. Vous devez donc utiliser l’attribut [FromBody] pour les types simples quand cette fonctionnalité est nécessaire.

Quand une action a plusieurs paramètres liés à partir du corps de la demande, une exception est levée. Par exemple, toutes les signatures de méthode d’action suivantes génèrent une exception :

  • [FromBody] est déduit sur les deux paramètres, car ce sont des types complexes.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Attribut [FromBody] sur un paramètre, déduit sur l’autre, car c’est un type complexe.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Attribut [FromBody] sur les deux paramètres.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Désactiver les règles d’inférence

Pour désactiver l’inférence de la source de liaison, définissez SuppressInferBindingSourcesForParameters sur true :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inférence de demande multipart/form-data

L’attribut [ApiController] applique une règle d’inférence pour les paramètres d’action de type IFormFile et IFormFileCollection. Le type de contenu de la demande multipart/form-data est déduit pour ces types.

Pour désactiver le comportement par défaut, définissez la propriété SuppressConsumesConstraintForFormFileParameters sur true :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Fonctionnalité Détails du problème pour les codes d’état erreur

MVC transforme un résultat d’erreur (résultat avec le code d’état 400 ou supérieur) en résultat avec ProblemDetails. Le type ProblemDetails est basé sur la spécification RFC 7807 pour fournir des détails de l’erreur lisibles par machine dans une réponse HTTP.

Prenons le code suivant dans une action de contrôleur :

if (pet == null)
{
    return NotFound();
}

La méthode NotFound produit un code d’état HTTP 404 avec un corps ProblemDetails. Par exemple :

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Désactiver la réponse ProblemDetails

La création automatique d’un ProblemDetails pour les codes d’état d’erreur est désactivée quand la propriété SuppressMapClientErrors est définie sur true :

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Définir les types de contenu de la demande pris en charge avec l’attribut [Consumes]

Par défaut, une action prend en charge tous les types de contenu de la demande disponibles. Par exemple, si une application est configurée pour prendre en charge à la fois les formateurs d’entréeJSON et XML, une action prend en charge plusieurs types de contenu, notamment application/json et application/xml.

L’attribut [Consumes] permet à une action de limiter les types de contenu de la demande pris en charge. Appliquez l’attribut [Consumes] à une action ou à un contrôleur, en spécifiant un ou plusieurs types de contenu :

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Dans le code précédent, l’action CreateProduct spécifie le type de contenu application/xml. Les demandes routées vers cette action doivent spécifier un en-tête Content-Type de application/xml. Les demandes qui ne spécifient pas un en-tête Content-Type de application/xml entraînent une réponse 415 Type de média non pris en charge.

L’attribut [Consumes] permet également à une action d’influencer la sélection en fonction du type de contenu de la demande entrante en appliquant une contrainte de type. Prenons l’exemple suivant :

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Dans le code précédent, ConsumesController est configuré pour gérer les demandes envoyées à l’URL https://localhost:5001/api/Consumes. Les actions du contrôleur PostJson et PostForm gèrent les demandes POST ayant la même URL. Sans l’attribut [Consumes] qui applique une contrainte de type, une exception de correspondance ambiguë est levée.

L’attribut [Consumes] est appliqué aux deux actions. L’action PostJson gère les demandes envoyées avec un en-tête Content-Type de application/json. L’action PostForm gère les demandes envoyées avec un en-tête Content-Type de application/x-www-form-urlencoded.

Ressources supplémentaires

ASP.NET Core prend en charge la création de services RESTful, également appelés API web, à l’aide de C#. Pour traiter les demandes, une API web utilise des contrôleurs. Les contrôleurs dans une API web sont des classes qui dérivent de ControllerBase. Cet article explique comment utiliser des contrôleurs pour gérer les demandes d’API web.

Affichez ou téléchargez l’exemple de code. (Guide pratique de téléchargement).

Classe ControllerBase

Une API web se compose d’une ou de plusieurs classes de contrôleur qui dérivent de ControllerBase. Le modèle de projet d’API web fournit un contrôleur de démarrage :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Ne créez pas de contrôleur d’API web en effectuant une dérivation de la classe Controller. Controller dérive de ControllerBase et ajoute la prise en charge pour les vues ; ainsi, il est destiné à la gestion des pages web, pas des demandes d’API web. Il existe une exception à cette règle : si vous prévoyez d’utiliser le même contrôleur pour les vues et les API web, il doit dériver de Controller.

La classe ControllerBase fournit de nombreuses propriétés et méthodes qui sont utiles pour gérer les requêtes HTTP. Par exemple, ControllerBase.CreatedAtAction retourne un code d’état 201 :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Voici d’autres exemples de méthodes fournies par ControllerBase.

Méthode Notes
BadRequest Retourne le code d’état 400.
NotFound Retourne le code d’état 404.
PhysicalFile Retourne un fichier.
TryUpdateModelAsync Appelle la liaison de modèle.
TryValidateModel Appelle la validation de modèle.

Pour obtenir la liste complète des méthodes et propriétés disponibles, consultez ControllerBase.

Attributs

L’espace de noms Microsoft.AspNetCore.Mvc fournit des attributs qui peuvent être utilisés pour configurer le comportement des contrôleurs d’API web et des méthodes d’action. L’exemple suivant utilise des attributs pour spécifier le verbe d’action HTTP pris en charge et l’ensemble des codes d’état HTTP connus qui peuvent être retournés :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Voici d’autres exemples d’attributs disponibles.

Attribut Notes
[Route] Spécifie le modèle d’URL pour un contrôleur ou une action.
[Bind] Spécifie le préfixe et les propriétés à inclure pour la liaison de modèle.
[HttpGet] Identifie une action qui prend en charge le verbe d’action HTTP GET.
[Consumes] Spécifie les types de données acceptés par une action.
[Produces] Spécifie les types de données retournés par une action.

Pour obtenir la liste des attributs disponibles, consultez l’espace de noms Microsoft.AspNetCore.Mvc.

Attribut ApiController

L’attribut [ApiController] peut être appliqué à une classe de contrôleur pour activer les comportements rigides spécifiques à l’API suivants :

Attribut sur des contrôleurs spécifiques

L’attribut [ApiController] peut être appliqué à des contrôleurs spécifiques, comme dans l’exemple suivant à partir du modèle de projet :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Attribut sur plusieurs contrôleurs

Une façon d’utiliser l’attribut sur plusieurs contrôleurs consiste à créer une classe de contrôleur de base personnalisée annotée avec l’attribut [ApiController]. L’exemple suivant montre une classe de base personnalisée et un contrôleur qui dérive de celle-ci :

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Attribut sur un assembly

L’attribut [ApiController] peut être appliqué à un assembly. De cette manière, l’annotation applique le comportement de l’API web à tous les contrôleurs de l’assembly. Les contrôleurs individuels n’ont aucun moyen de refuser. Appliquez l’attribut de niveau assembly à la déclaration d’espace de noms qui entoure la classe Startup :

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Exigence du routage d’attribut

L’attribut [ApiController] rend nécessaire le routage d’attributs. Par exemple :

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Les actions sont inaccessibles via itinéraires conventionnels définis par UseEndpoints, UseMvc ou UseMvcWithDefaultRoute dans Startup.Configure.

Réponses HTTP 400 automatiques

Grâce à l’attribut [ApiController], les erreurs de validation de modèles déclenchent automatiquement une réponse HTTP 400. Ainsi, le code suivant est inutile dans une méthode d’action :

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC utilise le filtre d’action ModelStateInvalidFilter pour effectuer la vérification précédente.

Réponse BadRequest par défaut

Le corps de la demande suivant est un exemple de type sérialisé :

{
  "": [
    "A non-empty request body is required."
  ]
}

Le type de réponse par défaut pour une réponse HTTP 400 est ValidationProblemDetails. Le corps de la demande suivant est un exemple de type sérialisé :

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Type de ValidationProblemDetails :

  • Fournit un format lisible par l’ordinateur permettant de spécifier les erreurs dans les réponses de l’API web.
  • Conforme à la spécification RFC 7807.

Pour assurer la cohérence des réponses automatiques et personnalisées, appelez la méthode ValidationProblem au lieu de BadRequest. ValidationProblem retourne un objet ValidationProblemDetails, ainsi que la réponse automatique.

Consigner automatiquement 400 réponses

Pour consigner les réponses automatiques 400, définissez la propriété de délégué InvalidModelStateResponseFactory pour effectuer un traitement personnalisé dans Startup.ConfigureServices. Par défaut, InvalidModelStateResponseFactory utilise ProblemDetailsFactory pour créer une instance ValidationProblemDetails.

L’exemple suivant montre comment récupérer une instance de ILogger<TCategoryName> pour enregistrer des informations sur une réponse automatique 400 :

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails response.
            // To produce a custom response, return a different implementation of IActionResult instead.
            return builtInFactory(context);
        };
    });

Désactiver la réponse 400 automatique

Pour désactiver le comportement 400 automatique, définissez la propriété SuppressModelStateInvalidFilter sur true. Ajoutez le code en surbrillance suivant dans Startup.ConfigureServices :

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Inférence de paramètre de source de liaison

Un attribut de source de liaison définit l’emplacement auquel se trouve la valeur d’un paramètre d’action. Les attributs de source de liaison suivants existent :

Attribut Source de liaison
[FromBody] Corps de la demande
[FromForm] Données de formulaire dans le corps de la demande
[FromHeader] En-tête de requête
[FromQuery] Paramètre de la chaîne de requête de la demande
[FromRoute] Données d’itinéraire à partir de la demande actuelle
[FromServices] Service de demande injecté comme paramètre d’action

Avertissement

N’utilisez pas [FromRoute] lorsque les valeurs risquent de contenir %2f (c’est-à-dire /). %2f ne sera pas sans la séquence d’échappement /. Utilisez [FromQuery] si la valeur peut contenir %2f.

Sans l’attribut [ApiController] ou des attributs de source de liaison comme [FromQuery], le runtime ASP.NET Core tente d’utiliser le classeur de modèles objet complexe. Le classeur de modèles objet complexe extrait des données à partir de fournisseurs de valeurs dans un ordre défini.

Dans l’exemple suivant, l’attribut [FromQuery] indique que la valeur du paramètre discontinuedOnly est fournie dans la chaîne de requête de l’URL de demande :

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L’attribut [ApiController] applique des règles d’inférence pour les sources de données par défaut des paramètres d’action. Ces règles vous évitent d’avoir à identifier les sources de liaison manuellement en appliquant des attributs aux paramètres d’action. Les règles d’inférence de source de liaison se comportent comme suit :

  • [FromBody] est déduit des paramètres de type complexe. Une exception à cette règle d’inférence [FromBody] est tout type complexe intégré ayant une signification spéciale, comme IFormCollection et CancellationToken. Le code de l’inférence de la source de liaison ignore ces types spéciaux.
  • [FromForm] est déduit pour les paramètres d’action de type IFormFile et IFormFileCollection. Il n’est pas déduit pour les types simples ou définis par l’utilisateur.
  • [FromRoute] est déduit pour tout nom de paramètre d’action correspondant à un paramètre dans le modèle d’itinéraire. Quand plus d’un itinéraire correspond à un paramètre d’action, toute valeur d’itinéraire est considérée comme [FromRoute].
  • [FromQuery] est déduit pour tous les autres paramètres d’action.

Notes sur l’inférence FromBody

[FromBody] n’est pas déduit pour les types simples tels que string ou int. Vous devez donc utiliser l’attribut [FromBody] pour les types simples quand cette fonctionnalité est nécessaire.

Quand une action a plusieurs paramètres liés à partir du corps de la demande, une exception est levée. Par exemple, toutes les signatures de méthode d’action suivantes génèrent une exception :

  • [FromBody] est déduit sur les deux paramètres, car ce sont des types complexes.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Attribut [FromBody] sur un paramètre, déduit sur l’autre, car c’est un type complexe.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Attribut [FromBody] sur les deux paramètres.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Désactiver les règles d’inférence

Pour désactiver l’inférence de la source de liaison, définissez SuppressInferBindingSourcesForParameters sur true. Ajoutez le code suivant dans Startup.ConfigureServices :

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Inférence de demande multipart/form-data

L’attribut [ApiController] applique une règle d’inférence pour les paramètres d’action de type IFormFile et IFormFileCollection. Le type de contenu de la demande multipart/form-data est déduit pour ces types.

Pour désactiver le comportement par défaut, définissez la propriété SuppressConsumesConstraintForFormFileParameters sur true dans Startup.ConfigureServices :

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Fonctionnalité Détails du problème pour les codes d’état erreur

MVC transforme un résultat d’erreur (résultat avec le code d’état 400 ou supérieur) en résultat avec ProblemDetails. Le type ProblemDetails est basé sur la spécification RFC 7807 pour fournir des détails de l’erreur lisibles par machine dans une réponse HTTP.

Prenons le code suivant dans une action de contrôleur :

if (pet == null)
{
    return NotFound();
}

La méthode NotFound produit un code d’état HTTP 404 avec un corps ProblemDetails. Par exemple :

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Désactiver la réponse ProblemDetails

La création automatique d’un ProblemDetails pour les codes d’état d’erreur est désactivée quand la propriété SuppressMapClientErrors est définie sur true. Ajoutez le code suivant dans Startup.ConfigureServices :

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Définir les types de contenu de la demande pris en charge avec l’attribut [Consumes]

Par défaut, une action prend en charge tous les types de contenu de la demande disponibles. Par exemple, si une application est configurée pour prendre en charge à la fois les formateurs d’entréeJSON et XML, une action prend en charge plusieurs types de contenu, notamment application/json et application/xml.

L’attribut [Consumes] permet à une action de limiter les types de contenu de la demande pris en charge. Appliquez l’attribut [Consumes] à une action ou à un contrôleur, en spécifiant un ou plusieurs types de contenu :

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Dans le code précédent, l’action CreateProduct spécifie le type de contenu application/xml. Les demandes routées vers cette action doivent spécifier un en-tête Content-Type de application/xml. Les demandes qui ne spécifient pas un en-tête Content-Type de application/xml entraînent une réponse 415 Type de média non pris en charge.

L’attribut [Consumes] permet également à une action d’influencer la sélection en fonction du type de contenu de la demande entrante en appliquant une contrainte de type. Prenons l’exemple suivant :

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Dans le code précédent, ConsumesController est configuré pour gérer les demandes envoyées à l’URL https://localhost:5001/api/Consumes. Les actions du contrôleur PostJson et PostForm gèrent les demandes POST ayant la même URL. Sans l’attribut [Consumes] qui applique une contrainte de type, une exception de correspondance ambiguë est levée.

L’attribut [Consumes] est appliqué aux deux actions. L’action PostJson gère les demandes envoyées avec un en-tête Content-Type de application/json. L’action PostForm gère les demandes envoyées avec un en-tête Content-Type de application/x-www-form-urlencoded.

Ressources supplémentaires

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Ne créez pas de contrôleur d’API web en effectuant une dérivation de la classe Controller. Controller dérive de ControllerBase et ajoute la prise en charge pour les vues ; ainsi, il est destiné à la gestion des pages web, pas des demandes d’API web. Il existe une exception à cette règle : si vous prévoyez d’utiliser le même contrôleur pour les vues et les API web, il doit dériver de Controller. La classe ControllerBase fournit de nombreuses propriétés et méthodes qui sont utiles pour gérer les requêtes HTTP. Par exemple, ControllerBase.CreatedAtAction retourne un code d’état 201 :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Voici d’autres exemples de méthodes fournies par ControllerBase :

Méthode Notes
BadRequest Retourne le code d’état 400.
NotFound Retourne le code d’état 404.
PhysicalFile Retourne un fichier.
TryUpdateModelAsync Appelle la liaison de modèle.
TryValidateModel Appelle la validation de modèle.

Pour obtenir la liste complète des méthodes et propriétés disponibles, consultez ControllerBase.

Attributs

L’espace de noms Microsoft.AspNetCore.Mvc fournit des attributs qui peuvent être utilisés pour configurer le comportement des contrôleurs d’API web et des méthodes d’action. L’exemple suivant utilise des attributs pour spécifier le verbe d’action HTTP pris en charge et l’ensemble des codes d’état HTTP connus qui peuvent être retournés :

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Voici des exemples supplémentaires d’attributs disponibles :

Attribut Notes
[Route] Spécifie le modèle d’URL pour un contrôleur ou une action.
[Bind] Spécifie le préfixe et les propriétés à inclure pour la liaison de modèle.
[HttpGet] Identifie une action qui prend en charge le verbe d’action HTTP GET.
[Consumes] Spécifie les types de données acceptés par une action.
[Produces] Spécifie les types de données retournés par une action.

Pour obtenir la liste des attributs disponibles, consultez l’espace de noms Microsoft.AspNetCore.Mvc.

Attribut ApiController

L’attribut [ApiController] peut être appliqué à une classe de contrôleur pour activer les comportements rigides spécifiques à l’API suivants :

Attribut sur des contrôleurs spécifiques

L’attribut [ApiController] peut être appliqué à des contrôleurs spécifiques, comme dans l’exemple suivant à partir du modèle de projet :

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Attribut sur plusieurs contrôleurs

Une façon d’utiliser l’attribut sur plusieurs contrôleurs consiste à créer une classe de contrôleur de base personnalisée annotée avec l’attribut [ApiController]. L’exemple suivant montre une classe de base personnalisée et un contrôleur qui dérive de celle-ci :

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("api/[controller]")]
public class PetsController : MyControllerBase

Attribut sur un assembly

Si la version de compatibilité est définie sur 2.2 ou une version ultérieure, l’attribut [ApiController] peut être appliqué à un assembly. De cette manière, l’annotation applique le comportement de l’API web à tous les contrôleurs de l’assembly. Les contrôleurs individuels n’ont aucun moyen de refuser. Appliquez l’attribut de niveau assembly à la déclaration d’espace de noms qui entoure la classe Startup :

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Exigence du routage d’attribut

L’attribut [ApiController] rend nécessaire le routage d’attributs. Par exemple :

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Les actions sont inaccessibles par le biais de routes conventionnelles définies par UseMvc ou UseMvcWithDefaultRoute dans Startup.Configure.

Réponses HTTP 400 automatiques

Grâce à l’attribut [ApiController], les erreurs de validation de modèles déclenchent automatiquement une réponse HTTP 400. Ainsi, le code suivant est inutile dans une méthode d’action :

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC utilise le filtre d’action ModelStateInvalidFilter pour effectuer la vérification précédente.

Réponse BadRequest par défaut

Avec une version de compatibilité 2.1, le type de réponse par défaut pour une réponse HTTP 400 est SerializableError. Le corps de la demande suivant est un exemple de type sérialisé :

{
  "": [
    "A non-empty request body is required."
  ]
}

Avec une version de compatibilité 2.2 ou ultérieure, le type de réponse par défaut pour une réponses HTTP 400 est ValidationProblemDetails. Le corps de la demande suivant est un exemple de type sérialisé :

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Type de ValidationProblemDetails :

  • Fournit un format lisible par l’ordinateur permettant de spécifier les erreurs dans les réponses de l’API web.
  • Conforme à la spécification RFC 7807.

Pour assurer la cohérence des réponses automatiques et personnalisées, appelez la méthode ValidationProblem au lieu de BadRequest. ValidationProblem retourne un objet ValidationProblemDetails, ainsi que la réponse automatique.

Consigner automatiquement 400 réponses

Voir Guide pratique pour consigner les réponses 400 automatiques en cas d’erreurs de validation de modèle (dotnet/AspNetCore.Docs#12157).

Désactiver la réponse 400 automatique

Pour désactiver le comportement 400 automatique, définissez la propriété SuppressModelStateInvalidFilter sur true. Ajoutez le code en surbrillance suivant dans Startup.ConfigureServices :

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Inférence de paramètre de source de liaison

Un attribut de source de liaison définit l’emplacement auquel se trouve la valeur d’un paramètre d’action. Les attributs de source de liaison suivants existent :

Attribut Source de liaison
[FromBody] Corps de la demande
[FromForm] Données de formulaire dans le corps de la demande
[FromHeader] En-tête de requête
[FromQuery] Paramètre de la chaîne de requête de la demande
[FromRoute] Données d’itinéraire à partir de la demande actuelle
[FromServices] Service de demande injecté comme paramètre d’action

Avertissement

N’utilisez pas [FromRoute] lorsque les valeurs risquent de contenir %2f (c’est-à-dire /). %2f ne sera pas sans la séquence d’échappement /. Utilisez [FromQuery] si la valeur peut contenir %2f. Sans l’attribut [ApiController] ou des attributs de source de liaison comme [FromQuery], le runtime ASP.NET Core tente d’utiliser le classeur de modèles objet complexe. Le classeur de modèles objet complexe extrait des données à partir de fournisseurs de valeurs dans un ordre défini.

Dans l’exemple suivant, l’attribut [FromQuery] indique que la valeur du paramètre discontinuedOnly est fournie dans la chaîne de requête de l’URL de demande :

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L’attribut [ApiController] applique des règles d’inférence pour les sources de données par défaut des paramètres d’action. Ces règles vous évitent d’avoir à identifier les sources de liaison manuellement en appliquant des attributs aux paramètres d’action. Les règles d’inférence de source de liaison se comportent comme suit :

  • [FromBody] est déduit des paramètres de type complexe. Une exception à cette règle d’inférence [FromBody] est tout type complexe intégré ayant une signification spéciale, comme IFormCollection et CancellationToken. Le code de l’inférence de la source de liaison ignore ces types spéciaux.
  • [FromForm] est déduit pour les paramètres d’action de type IFormFile et IFormFileCollection. Il n’est pas déduit pour les types simples ou définis par l’utilisateur.
  • [FromRoute] est déduit pour tout nom de paramètre d’action correspondant à un paramètre dans le modèle d’itinéraire. Quand plus d’un itinéraire correspond à un paramètre d’action, toute valeur d’itinéraire est considérée comme [FromRoute].
  • [FromQuery] est déduit pour tous les autres paramètres d’action.

Notes sur l’inférence FromBody

[FromBody] n’est pas déduit pour les types simples tels que string ou int. Vous devez donc utiliser l’attribut [FromBody] pour les types simples quand cette fonctionnalité est nécessaire.

Quand une action a plusieurs paramètres liés à partir du corps de la demande, une exception est levée. Par exemple, toutes les signatures de méthode d’action suivantes génèrent une exception :

  • [FromBody] est déduit sur les deux paramètres, car ce sont des types complexes.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Attribut [FromBody] sur un paramètre, déduit sur l’autre, car c’est un type complexe.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Attribut [FromBody] sur les deux paramètres.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Remarque

Dans ASP.NET Core 2.1, les paramètres de type collection comme les listes et les tableaux sont déduits de manière incorrecte en tant que [FromQuery]. L’attribut [FromBody] doit être utilisé pour ces paramètres s’ils doivent être liés à partir du corps de la demande. Ce problème est corrigé dans ASP.NET Core version 2.2 ou ultérieure, où les paramètres de type collection sont déduits pour être liés à partir du corps par défaut.

Désactiver les règles d’inférence

Pour désactiver l’inférence de la source de liaison, définissez SuppressInferBindingSourcesForParameters sur true. Ajoutez le code suivant dans Startup.ConfigureServices :

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Inférence de demande multipart/form-data

L’attribut [ApiController] applique une règle d’inférence pour les paramètres d’action de type IFormFile et IFormFileCollection. Le type de contenu de la demande multipart/form-data est déduit pour ces types. Pour désactiver le comportement par défaut, définissez la propriété SuppressConsumesConstraintForFormFileParameters sur true dans Startup.ConfigureServices :

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Fonctionnalité Détails du problème pour les codes d’état erreur

Quand la version de compatibilité est 2.2 ou ultérieure, MVC transforme un résultat d’erreur (résultat avec un code d’état égal ou supérieur à 400) en résultat avec ProblemDetails. Le type ProblemDetails est basé sur la spécification RFC 7807 pour fournir des détails de l’erreur lisibles par machine dans une réponse HTTP. Prenons le code suivant dans une action de contrôleur :

if (pet == null)
{
    return NotFound();
}

La méthode NotFound produit un code d’état HTTP 404 avec un corps ProblemDetails. Par exemple :

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Désactiver la réponse ProblemDetails

La création automatique d’un ProblemDetails pour les codes d’état d’erreur est désactivée quand la propriété SuppressMapClientErrors est définie sur true. Ajoutez le code suivant dans Startup.ConfigureServices :

Définir les types de contenu de la demande pris en charge avec l’attribut [Consumes]

Par défaut, une action prend en charge tous les types de contenu de la demande disponibles. Par exemple, si une application est configurée pour prendre en charge à la fois les formateurs d’entréeJSON et XML, une action prend en charge plusieurs types de contenu, notamment application/json et application/xml.

L’attribut [Consumes] permet à une action de limiter les types de contenu de la demande pris en charge. Appliquez l’attribut [Consumes] à une action ou à un contrôleur, en spécifiant un ou plusieurs types de contenu :

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Dans le code précédent, l’action CreateProduct spécifie le type de contenu application/xml. Les demandes routées vers cette action doivent spécifier un en-tête Content-Type de application/xml. Les demandes qui ne spécifient pas un en-tête Content-Type de application/xml entraînent une réponse 415 Type de média non pris en charge. L’attribut [Consumes] permet également à une action d’influencer la sélection en fonction du type de contenu de la demande entrante en appliquant une contrainte de type. Prenons l’exemple suivant :

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Dans le code précédent, ConsumesController est configuré pour gérer les demandes envoyées à l’URL https://localhost:5001/api/Consumes. Les actions du contrôleur PostJson et PostForm gèrent les demandes POST ayant la même URL. Sans l’attribut [Consumes] qui applique une contrainte de type, une exception de correspondance ambiguë est levée. L’attribut [Consumes] est appliqué aux deux actions. L’action PostJson gère les demandes envoyées avec un en-tête Content-Type de application/json. L’action PostForm gère les demandes envoyées avec un en-tête Content-Type de application/x-www-form-urlencoded.

Ressources supplémentaires