Générer des documents OpenAPI
Le package Microsoft.AspNetCore.OpenApi
fournit une prise en charge intégrée de la génération de documents OpenAPI dans ASP.NET Core. Le package offre les fonctionnalités suivantes :
- Prise en charge de la génération de documents OpenAPI au moment de l’exécution et de leur accès via un point de terminaison au niveau de l’application
- Prise en charge des API de type « transformateur » qui permettent de modifier le document généré
- Prise en charge de la génération de plusieurs documents OpenAPI à partir d’une seule application
- Tire parti de la prise en charge du schéma JSON fournie par
System.Text.Json
. - Est compatible avec AoT natif.
Installation de package
Installez le package Microsoft.AspNetCore.OpenApi
:
Exécutez la commande suivante à partir de la console Gestionnaire de package:
Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease
Pour ajouter la prise en charge de la génération de documents OpenAPI au moment de la génération, installez le package Microsoft.Extensions.ApiDescription.Server
:
Exécutez la commande suivante à partir de la console Gestionnaire de package:
Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease
Configurer la génération de document OpenAPI
Le code suivant :
- Ajoute des services OpenAPI.
- Active le point de terminaison pour afficher le document OpenAPI au format JSON.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Lancez l’application et accédez à https://localhost:<port>/openapi/v1.json
pour afficher le document OpenAPI généré.
Inclure des métadonnées OpenAPI dans une application web ASP.NET
Inclusion de métadonnées OpenAPI pour les points de terminaison
ASP.NET collecte les métadonnées à partir des points de terminaison de l’application web et les utilise pour générer un document OpenAPI.
Dans les applications basées sur un contrôleur, les métadonnées sont collectées à partir d’attributs tels que [EndpointDescription]
, [HttpPost]
et [Produces]
.
Dans des API minimales, les métadonnées peuvent être collectées à partir d’attributs, mais peuvent également être définies à l’aide de méthodes d’extension et d’autres stratégies, telles que le retour de TypedResults à partir de gestionnaires de routage.
Le tableau suivant fournit une vue d’ensemble des métadonnées collectées et des stratégies utilisées pour les définir.
Métadonnées | Attribut | Méthode d’extension | Autres stratégies |
---|---|---|---|
résumé | [EndpointSummary] |
WithSummary | |
description | [EndpointDescription] |
WithDescription | |
tags | [Tags] |
WithTags | |
operationId | [EndpointName] |
WithName | |
parameters | [FromQuery] , , [FromRoute] [FromHeader] , ,[FromForm] |
||
Description des paramètres | [EndpointDescription] |
||
requestBody | [FromBody] |
Accepts | |
responses | [Produces] |
Produces, ProducesProblem | TypedResults |
Exclure des points de terminaison | [ExcludeFromDescription] , [ApiExplorerSettings] |
ExcludeFromDescription |
ASP.NET Core ne collecte pas les métadonnées à partir de commentaires de documentation XML.
Les sections suivantes montrent comment inclure des métadonnées dans une application pour personnaliser le document OpenAPI généré.
Résumé et description
Le résumé et la description du point de terminaison peuvent être définis à l’aide des attributs [EndpointSummary]
et [EndpointDescription]
, ou, dans des API minimales, à l’aide des méthodes d’extension WithSummary et WithDescription.
L’exemple suivant illustre les différentes stratégies pour définir des résumés et des descriptions.
Notez que les attributs sont placés sur la méthode déléguée et non sur la méthode app.MapGet.
app.MapGet("/extension-methods", () => "Hello world!")
.WithSummary("This is a summary.")
.WithDescription("This is a description.");
app.MapGet("/attributes",
[EndpointSummary("This is a summary.")]
[EndpointDescription("This is a description.")]
() => "Hello world!");
tags
OpenAPI prend en charge la spécification d’étiquettes sur chaque point de terminaison en tant que forme de catégorisation.
Dans des API minimales, les étiquettes peuvent être définies à l’aide de l’attribut [Tags]
ou de la méthode d’extension WithTags.
L’exemple suivant illustre les différentes stratégies pour définir des étiquettes.
app.MapGet("/extension-methods", () => "Hello world!")
.WithTags("todos", "projects");
app.MapGet("/attributes",
[Tags("todos", "projects")]
() => "Hello world!");
operationId
OpenAPI prend en charge un operationId sur chaque point de terminaison en tant qu’identificateur ou nom unique pour l’opération.
Dans des API minimales, l’operationId peut être défini à l’aide de l’attribut [EndpointName]
ou de la méthode d’extension WithName.
L’exemple suivant illustre les différentes stratégies pour définir l’operationId.
app.MapGet("/extension-methods", () => "Hello world!")
.WithName("FromExtensionMethods");
app.MapGet("/attributes",
[EndpointName("FromAttributes")]
() => "Hello world!");
parameters
OpenAPI prend en charge l’annotation du chemin, de la chaîne de requête, de l’en-tête et des paramètres cookie consommés par une API.
L’infrastructure déduit automatiquement les types des paramètres de requête en fonction de la signature du gestionnaire de routage.
L’attribut [EndpointDescription]
peut être utilisé pour fournir une description d’un paramètre.
L’exemple suivant montre comment définir une description pour un paramètre.
app.MapGet("/attributes",
([Description("This is a description.")] string name) => "Hello world!");
Décrire le corps de la demande
Le requestBody
champ dans OpenAPI décrit le corps d’une requête qu’un client d’API peut envoyer au serveur, y compris les types de contenu pris en charge et le schéma du contenu du corps.
Lorsque la méthode du gestionnaire de points de terminaison accepte les paramètres liés à partir du corps de la requête, ASP.NET Core génère un correspondant requestBody
pour l’opération dans le document OpenAPI. Les métadonnées du corps de la demande peuvent également être spécifiées à l’aide d’attributs ou de méthodes d’extension. Des métadonnées supplémentaires peuvent être définies avec un transformateur de document ou un transformateur d’opération.
Si le point de terminaison ne définit aucun paramètre lié au corps de la requête, mais utilise plutôt le corps de la requête directement, ASP.NET Core fournit des mécanismes pour spécifier les métadonnées du corps de HttpContext la requête. Il s’agit d’un scénario courant pour les points de terminaison qui traitent le corps de la requête en tant que flux.
Certaines métadonnées du corps de la requête peuvent être déterminées à partir des FromBody
paramètres de FromForm
la méthode de gestionnaire de routage.
Une description du corps de la demande peut être définie avec un [Description]
attribut sur le paramètre avec FromBody
ou FromForm
.
Si le FromBody
paramètre n’est pas nullable et EmptyBodyBehavior n’est pas défini Allow dans l’attribut, le corps de la FromBody
requête est requis et le required
champ de celui-ci requestBody
est défini true
dans le document OpenAPI généré.
Les corps de formulaire sont toujours requis et ont required
la valeur true
.
Utilisez un transformateur de document ou un transformateur d’opération pour définir les example
champs, examples
ou encoding
pour ajouter des extensions de spécification pour le corps de la requête dans le document OpenAPI généré.
D’autres mécanismes permettant de définir les métadonnées du corps de la demande dépendent du type d’application en cours de développement et sont décrits dans les sections suivantes.
Les types de contenu du corps de la requête dans le document OpenAPI généré sont déterminés à partir du type du paramètre lié au corps de la requête ou spécifié avec la Accepts méthode d’extension.
Par défaut, le type de contenu d’un FromBody
paramètre sera application/json
et le type de contenu pour FromForm
les paramètres sera multipart/form-data
ou application/x-www-form-urlencoded
.
La prise en charge de ces types de contenu par défaut est intégrée aux API minimales, et d’autres types de contenu peuvent être gérés à l’aide de la liaison personnalisée. Pour plus d’informations, consultez la rubrique de liaison personnalisée de la documentation sur les API minimales.
Il existe plusieurs façons de spécifier un type de contenu différent pour le corps de la demande.
Si le type du paramètre implémente IEndpointParameterMetadataProvider, ASP.NET Core utilise cette interface pour déterminer le ou les types de FromBody
contenu dans le corps de la requête.
L’infrastructure utilise la PopulateMetadata méthode de cette interface pour définir les types de contenu et le type du contenu du corps du corps de la requête. Par exemple, une Todo
classe qui accepte ou application/xml
text/xml
type de contenu peut être utilisée IEndpointParameterMetadataProvider pour fournir ces informations à l’infrastructure.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo)));
}
}
La Accepts méthode d’extension peut également être utilisée pour spécifier le type de contenu du corps de la requête.
Dans l’exemple suivant, le point de terminaison accepte un objet Todo
dans le corps de la requête avec un type de contenu attendu de application/xml
.
app.MapPut("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
Étant application/xml
donné qu’il ne s’agit pas d’un type de contenu intégré, la Todo
classe doit implémenter l’interface IBindableFromHttpContext<TSelf> pour fournir une liaison personnalisée pour le corps de la requête. Par exemple :
public class Todo : IBindableFromHttpContext<Todo>
{
public static async ValueTask<Todo?> BindAsync(HttpContext context, ParameterInfo parameter)
{
var xmlDoc = await XDocument.LoadAsync(context.Request.Body, LoadOptions.None, context.RequestAborted);
var serializer = new XmlSerializer(typeof(Todo));
return (Todo?)serializer.Deserialize(xmlDoc.CreateReader());
}
Si le point de terminaison ne définit aucun paramètre lié au corps de la demande, utilisez la Accepts méthode d’extension pour spécifier le type de contenu accepté par le point de terminaison.
Si vous spécifiez <AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepte%2A> plusieurs fois, seules les métadonnées du dernier sont utilisées : elles ne sont pas combinées.
Décrire des types de réponse
OpenAPI prend en charge la description des réponses retournées par une API. ASP.NET Core fournit plusieurs stratégies pour définir les métadonnées de réponse d’un point de terminaison. Les métadonnées de réponse qui peuvent être définies incluent le code d’état, le type du corps de la réponse et les types de contenu d’une réponse. Les réponses dans OpenAPI peuvent avoir des métadonnées supplémentaires, telles que la description, les en-têtes, les liens et les exemples. Ces métadonnées supplémentaires peuvent être définies avec un transformateur de document ou un transformateur d’opération.
Les mécanismes spécifiques de définition des métadonnées de réponse dépendent du type d’application en cours de développement.
Dans les applications API minimales, ASP.NET Core peut extraire les métadonnées de réponse ajoutées par les méthodes d’extension sur le point de terminaison, les attributs du gestionnaire d’itinéraires et le type de retour du gestionnaire d’itinéraires.
- La Produces méthode d’extension peut être utilisée sur le point de terminaison pour spécifier le code d’état, le type du corps de la réponse et les types de contenu d’une réponse à partir d’un point de terminaison.
- L’attribut
[ProducesResponseType]
peut ProducesResponseTypeAttribute<T> être utilisé pour spécifier le type du corps de la réponse. - Un gestionnaire de routage peut être utilisé pour renvoyer un type qui implémente IEndpointMetadataProvider pour spécifier le ou les types de contenu du corps de la réponse.
- La ProducesProblem méthode d’extension sur le point de terminaison peut être utilisée pour spécifier le code d’état et les types de contenu d’une réponse d’erreur.
Notez que les méthodes d’extension et ProducesProblem les méthodes sont prises en charge à la Produces fois RouteHandlerBuilder sur et sur RouteGroupBuilder. Cela permet, par exemple, de définir un ensemble commun de réponses d’erreur pour toutes les opérations d’un groupe.
Lorsqu’il n’est pas spécifié par l’une des stratégies précédentes, le :
- Le code d’état de la réponse est défini par défaut sur 200.
- Le schéma du corps de la réponse peut être déduit du type de retour implicite ou explicite de la méthode de point de terminaison, par exemple à partir de
T
; Task<TResult>sinon, il est considéré comme non spécifié. - Le type de contenu pour le corps de réponse spécifié ou déduit est « application/json ».
Dans les API minimales, la Produces méthode d’extension et l’attribut [ProducesResponseType]
définissent uniquement les métadonnées de réponse pour le point de terminaison. Ils ne modifient ni ne limitent pas le comportement du point de terminaison, qui peut retourner un code d’état ou un type de corps de réponse différent de celui spécifié par les métadonnées, et le type de contenu est déterminé par le type de retour de la méthode de gestionnaire de routage, quel que soit le type de contenu spécifié dans les attributs ou les méthodes d’extension.
La Produces méthode d’extension peut spécifier le type de réponse d’un point de terminaison, avec un code d’état par défaut de 200 et un type de contenu par défaut de application/json
. L'exemple suivant illustre ce mécanisme :
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
Vous [ProducesResponseType]
pouvez l’utiliser pour ajouter des métadonnées de réponse à un point de terminaison. Notez que l’attribut est appliqué à la méthode du gestionnaire de routage, et non à l’appel de méthode pour créer l’itinéraire, comme illustré dans l’exemple suivant :
app.MapGet("/todos",
[ProducesResponseType<List<Todo>>(200)]
async (TodoDb db) => await db.Todos.ToListAsync());
L’utilisation de TypedResults dans l’implémentation du gestionnaire de routage d’un point de terminaison inclut automatiquement les métadonnées du type de réponse pour le point de terminaison. Par exemple, le code suivant annote automatiquement le point de terminaison avec une réponse sous le code d’état 200
avec un type de contenu application/json
.
app.MapGet("/todos", async (TodoDb db) =>
{
var todos = await db.Todos.ToListAsync();
return TypedResults.Ok(todos);
});
Seuls les types de retour qui implémentent IEndpointMetadataProvider la création d’une responses
entrée dans le document OpenAPI. Voici une liste partielle de certaines méthodes d’assistance TypedResults qui produisent une responses
entrée :
Méthode d’assistance TypedResults | code d’état |
---|---|
Ok() | 200 |
Created() | 201 |
CreatedAtRoute() | 201 |
Accepté() | 202 |
AcceptedAtRoute() | 202 |
NoContent() | 204 |
BadRequest() | 400 |
ValidationProblem() | 400 |
NotFound() | 404 |
Conflit() | 409 |
UnprocessableEntity() | 422 |
Toutes ces méthodes, à l’exception NoContent
d’une surcharge générique qui spécifie le type du corps de la réponse.
Une classe peut être implémentée pour définir les métadonnées du point de terminaison et la retourner à partir du gestionnaire de routage.
Définir des réponses pour ProblemDetails
Lorsque vous définissez le type de réponse pour les points de terminaison susceptibles de renvoyer une réponse ProblemDetails, vous pouvez utiliser ce qui suit pour ajouter les métadonnées de réponse appropriées pour le point de terminaison :
- ProducesProblem
- La méthode d’extension ProducesValidationProblem.
- TypedResults avec un code d’état dans la plage (400-499).
Pour plus d’informations sur la configuration d’une application API minimale pour retourner des réponses ProblemDetails, consultez Gérer les erreurs dans des API minimales.
Types de réponse multiples
Si un point de terminaison peut retourner différents types de réponse dans différents scénarios, vous pouvez fournir des métadonnées des manières suivantes :
Appeler la méthode d’extension Produces plusieurs fois, comme illustré dans l’exemple suivant :
app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) => await db.Todos.FindAsync(id) is Todo todo ? Results.Ok(todo) : Results.NotFound()) .Produces<Todo>(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound);
Utiliser Results<TResult1,TResult2,TResult3,TResult4,TResult5,TResult6> dans la signature et TypedResults dans le corps du gestionnaire, comme illustré dans l’exemple suivant :
app.MapGet("/book/{id}", Results<Ok<Book>, NotFound> (int id, List<Book> bookList) => { return bookList.FirstOrDefault((i) => i.Id == id) is Book book ? TypedResults.Ok(book) : TypedResults.NotFound(); });
Les
Results<TResult1,TResult2,TResultN>
types d’union déclarent qu’un gestionnaire de routage retourne plusieurs types concrets implémentantIResult
, et tous les types qui implémentent IEndpointMetadataProvider contribueront aux métadonnées du point de terminaison.Les types d’union implémentent des opérateurs de cast implicites. Ces opérateurs permettent au compilateur de convertir automatiquement les types spécifiés dans les arguments génériques en instance du type union. Cette fonctionnalité présente l’avantage supplémentaire de vérifier au moment de la compilation qu’un gestionnaire de routage retourne uniquement les résultats qu’il déclare faire. La tentative de retourner un type qui n’est pas déclaré comme l’un des arguments génériques vers
Results<TResult1,TResult2,TResultN>
entraîne une erreur de compilation.
Exclusion des points de terminaison du document généré
Par défaut, tous les points de terminaison définis dans une application sont documentés dans le fichier OpenAPI généré, mais les points de terminaison peuvent être exclus du document à l’aide d’attributs ou de méthodes d’extension.
Le mécanisme permettant de spécifier un point de terminaison qui doit être exclu dépend du type d’application en cours de développement.
Les API minimales prennent en charge deux stratégies pour exclure un point de terminaison donné du document OpenAPI :
- ExcludeFromDescription méthode d’extension
- Attribut
[ExcludeFromDescription]
L’exemple suivant illustre les différentes stratégies permettant d’exclure un point de terminaison donné du document OpenAPI généré.
app.MapGet("/extension-method", () => "Hello world!")
.ExcludeFromDescription();
app.MapGet("/attributes",
[ExcludeFromDescription]
() => "Hello world!");
Inclusion des métadonnées OpenAPI pour les types de données
Les classes ou enregistrements C# utilisés dans les corps de requête ou de réponse sont représentés en tant que schémas dans le document OpenAPI généré. Par défaut, seules les propriétés publiques sont représentées dans le schéma, mais des JsonSerializerOptions permettent également de créer des propriétés de schéma pour les champs.
Lorsque PropertyNamingPolicy est définie sur camel-case (il s’agit de la valeur par défaut dans les applications web ASP.NET), les noms de propriétés dans un schéma correspondent à la forme camel-case du nom de propriété de la classe ou de l’enregistrement.
[JsonPropertyName]
peut être utilisé sur une propriété individuelle pour spécifier le nom de la propriété dans le schéma.
type et format
La bibliothèque de schémas JSON mappe les types C# standard sur le type
et le format
OpenAPI comme suit :
Type C# | type OpenAPI |
format OpenAPI |
---|---|---|
int | entier | int32 |
long | entier | int64 |
short | entier | int16 |
byte | entier | uint8 |
virgule flottante | nombre | virgule flottante |
double | nombre | double |
decimal | nombre | double |
bool | booléen | |
string | string | |
char | string | char |
byte[] | string | byte |
DateTimeOffset | string | date-time |
DateOnly | string | date |
TimeOnly | string | time |
Uri | string | URI |
Guid | string | uuid |
object | omis | |
dynamic | omis |
Notez que les types objet et dynamique n’ont pas de type défini dans OpenAPI, car ils peuvent contenir des données de n’importe quel type, y compris des types primitifs comme int ou string.
Le type
et le format
peuvent également être définis avec un transformateur de schéma. Par exemple, vous souhaiterez peut-être que le format
des types décimaux soit decimal
au lieu de double
.
Utilisation d’attributs pour ajouter des métadonnées
ASP.NET utilise les métadonnées des attributs de propriétés de classe ou d’enregistrement pour définir des métadonnées sur les propriétés correspondantes du schéma généré.
Le tableau suivant récapitule les attributs de l’espace de noms System.ComponentModel
qui fournissent des métadonnées pour le schéma généré :
Attribut | Description |
---|---|
[Description] |
Définit le description d’une propriété dans le schéma. |
[Required] |
Marque une propriété comme required dans le schéma. |
[DefaultValue] |
Définit la valeur default d’une propriété dans le schéma. |
[Range] |
Définit les valeurs minimum et maximum d’un entier ou d’un nombre. |
[MinLength] |
Définit la valeur minLength d’une chaîne. |
[MaxLength] |
Définit la valeur maxLength d’une chaîne. |
[RegularExpression] |
Définit la valeur pattern d’une chaîne. |
Notez que dans les applications basées sur un contrôleur, ces attributs ajoutent des filtres à l’opération pour vérifier que toutes les données entrantes répondent aux contraintes. Dans les API minimales, ces attributs définissent les métadonnées dans le schéma généré, mais la validation doit être effectuée explicitement via un filtre de point de terminaison, dans la logique du gestionnaire de routage ou via un package tiers.
Autres sources de métadonnées pour les schémas générés
requis
Les propriétés peuvent également être marquées comme required
avec le modificateur requis.
enum
Les types enum en C# sont basés sur des entiers, mais peuvent être représentés sous forme de JSON avec un [JsonConverter]
et un JsonStringEnumConverter. Lorsqu’un type enum est représenté sous la forme d’une chaîne dans JSON, le schéma généré a une propriété enum
avec les valeurs de chaîne de l’enum.
Un type enum sans [JsonConverter]
sera défini comme type: integer
dans le schéma généré.
Remarque : [AllowedValues]
ne définit pas les valeurs enum
de la propriété.
nullable
Le type référence ou les propriétés définies en tant que valeur pouvant accepter la valeur Null contiennent nullable: true
dans le schéma généré. Cela est cohérent avec le comportement par défaut du désérialiseur System.Text.Json, qui accepte null
comme valeur valide pour une propriété acceptant la valeur Null.
additionalProperties
Les schémas sont générés sans assertion additionalProperties
par défaut, ce qui implique la valeur par défaut true
. Cela est cohérent avec le comportement par défaut du désérialiseur System.Text.Json, qui ignore silencieusement les propriétés supplémentaires dans un objet JSON.
Si les propriétés supplémentaires d’un schéma ne doivent avoir que des valeurs d’un type spécifique, définissez la propriété ou la classe en tant que Dictionary<string, type>
. Le type de clé du dictionnaire doit être string
. Cela génère un schéma avec la valeur additionalProperties
spécifiant le schéma de « type » comme types valeur requis.
Métadonnées pour les types polymorphes
Utilisez les attributs [JsonPolymorphic]
et [JsonDerivedType]
sur une classe parente pour spécifier le champ de discriminateur et les sous-types pour un type polymorphe.
[JsonDerivedType]
ajoute un champ de discriminateur au schéma pour chaque sous-classe, avec une enum spécifiant la valeur de discriminateur spécifique à la sous-classe. Cet attribut modifie également le constructeur de chaque classe dérivée pour définir la valeur de discriminateur.
Une classe abstraite avec un attribut [JsonPolymorphic]
possède un champ discriminator
dans le schéma, mais une classe concrète avec un attribut [JsonPolymorphic]
ne possède pas de champ discriminator
. OpenAPI exige que la propriété de discriminateur soit une propriété obligatoire dans le schéma, mais étant donné qu’elle n’est pas définie dans la classe de base concrète, le schéma ne peut pas inclure de champ discriminator
.
Ajout de métadonnées avec un transformateur de schéma
Un transformateur de schéma peut être utilisé pour remplacer toute métadonnée par défaut ou ajouter des métadonnées supplémentaires, telles que des valeurs example
, au schéma généré. Pour plus d’informations, consultez Utiliser des transformateurs de schéma.
Options pour personnaliser la génération de document OpenAPI
Les sections suivantes montrent comment personnaliser la génération de document OpenAPI.
Personnaliser le nom du document OpenAPI
Chaque document OpenAPI dans une application a un nom unique. Le nom du document par défaut inscrit est v1
.
builder.Services.AddOpenApi(); // Document name is v1
Le nom du document peut être modifié en passant le nom en tant que paramètre à l’appel AddOpenApi
.
builder.Services.AddOpenApi("internal"); // Document name is internal
Le nom du document s’affiche à plusieurs endroits dans l’implémentation OpenAPI.
Lors de l’extraction du document OpenAPI généré, le nom du document est fourni en tant qu’argument de paramètre documentName
dans la requête. Les requêtes suivantes résolvent les documents v1
et internal
.
GET http://localhost:5000/openapi/v1.json
GET http://localhost:5000/openapi/internal.json
Personnaliser la version OpenAPI d’un document généré
Par défaut, la génération de document OpenAPI crée un document conforme à la version 3.0 de la spécification OpenAPI. Le code suivant montre comment modifier la version par défaut du document OpenAPI :
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
});
Personnaliser l’itinéraire du point de terminaison OpenAPI
Par défaut, le point de terminaison OpenAPI inscrit via un appel à MapOpenApi expose le document au niveau du point de terminaison /openapi/{documentName}.json
. Le code suivant montre comment personnaliser l’itinéraire au niveau duquel le document OpenAPI est inscrit :
app.MapOpenApi("/openapi/{documentName}/openapi.json");
Bien que ce ne soit pas recommandé, il est possible de supprimer le paramètre de routage documentName
de l’itinéraire du point de terminaison. Lorsque le paramètre d’itinéraire documentName
est supprimé de l’itinéraire du point de terminaison, l’infrastructure tente de résoudre le nom du document à partir du paramètre de requête. Ne pas fournir le documentName
dans l’itinéraire ou la requête peut entraîner un comportement inattendu.
Personnaliser le point de terminaison OpenAPI
Étant donné que le document OpenAPI est fourni via un point de terminaison de gestionnaire de routage, toute personnalisation disponible pour les points de terminaison minimaux standard l’est aussi pour le point de terminaison OpenAPI.
Limiter l’accès au document OpenAPI aux utilisateurs autorisés
Par défaut, le point de terminaison OpenAPI n’active aucune vérification des autorisations. Toutefois, des vérifications d’autorisation peuvent être appliquées au document OpenAPI. Dans le code suivant, l’accès au document OpenAPI est limité aux utilisateurs disposant du rôle tester
:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization(o =>
{
o.AddPolicy("ApiTesterPolicy", b => b.RequireRole("tester"));
});
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi()
.RequireAuthorization("ApiTesterPolicy");
app.MapGet("/", () => "Hello world!");
app.Run();
Mettre en cache le document OpenAPI généré
Le document OpenAPI est régénéré chaque fois qu’une requête est envoyée au point de terminaison OpenAPI. La régénération permet aux transformateurs d’incorporer l’état de l’application dynamique dans leur fonctionnement. Par exemple,vous pouvez régénérer une requête avec les détails du contexte HTTP. Le cas échéant, le document OpenAPI peut être mis en cache pour éviter d’exécuter le pipeline de génération de documents sur chaque requête HTTP.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromMinutes(10)));
});
builder.Services.AddOpenApi();
var app = builder.Build();
app.UseOutputCache();
app.MapOpenApi()
.CacheOutput();
app.MapGet("/", () => "Hello world!");
app.Run();
Transformateurs de documents OpenAPI
Cette section montre comment personnaliser des documents OpenAPI avec des transformateurs.
Personnaliser des documents OpenAPI avec des transformateurs
Les transformateurs fournissent une API permettant de modifier le document OpenAPI avec des personnalisations définies par l’utilisateur. Les transformateurs sont utiles dans des scénarios tels que les suivants :
- Ajout de paramètres à toutes les opérations d’un document.
- Modification des descriptions des paramètres ou des opérations.
- Ajout d’informations de premier niveau au document OpenAPI.
Les transformateurs se répartissent en trois catégories :
- Les transformateurs de documents ont accès à l’intégralité du document OpenAPI. Vous pouvez les utiliser pour apporter des modifications globales au document.
- Les transformateurs d’opérations s’appliquent à chaque opération individuelle. Chaque opération individuelle combine un chemin d’accès et une méthode HTTP. Vous pouvez les utiliser pour modifier des paramètres ou des réponses sur des points de terminaison.
- Les transformateurs de schéma s’appliquent à chaque schéma du document. Ils peuvent être utilisés pour modifier le schéma des corps de requête ou de réponse, ou tout schéma imbriqué.
Les transformateurs peuvent être inscrits sur le document en appelant la méthode AddDocumentTransformer sur l’objet OpenApiOptions. L’extrait de code suivant montre différentes façons d’inscrire des transformateurs sur le document :
- Inscrire un transformateur de document à l’aide d’un délégué.
- Inscrire un transformateur de document à l’aide d’une instance de IOpenApiDocumentTransformer.
- Inscrire un transformateur de document à l’aide d’un IOpenApiDocumentTransformer avec injection de dépendance activée.
- Inscrire un transformateur d’opération à l’aide d’un délégué.
- Inscrire un transformateur d’opération à l’aide d’une instance de IOpenApiOperationTransformer.
- Inscrire un transformateur d’opération à l’aide d’un IOpenApiOperationTransformer avec injection de dépendance activée.
- Inscrire un transformateur de schéma à l’aide d’un délégué.
- Inscrire un transformateur de schéma à l’aide d’une instance de IOpenApiSchemaTransformer.
- Inscrire un transformateur de schéma à l’aide d’un IOpenApiSchemaTransformer avec injection de dépendance activée.
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken)
=> Task.CompletedTask);
options.AddDocumentTransformer(new MyDocumentTransformer());
options.AddDocumentTransformer<MyDocumentTransformer>();
options.AddOperationTransformer((operation, context, cancellationToken)
=> Task.CompletedTask);
options.AddOperationTransformer(new MyOperationTransformer());
options.AddOperationTransformer<MyOperationTransformer>();
options.AddSchemaTransformer((schema, context, cancellationToken)
=> Task.CompletedTask);
options.AddSchemaTransformer(new MySchemaTransformer());
options.AddSchemaTransformer<MySchemaTransformer>();
});
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Ordre d’exécution pour les transformateurs
Les transformateurs s’exécutent dans un ordre de premier entré, premier sorti (FIFO) en fonction de l’inscription. Dans l’extrait de code suivant, le transformateur de document a accès aux modifications apportées par le transformateur d’opération :
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer((operation, context, cancellationToken)
=> Task.CompletedTask);
options.AddDocumentTransformer((document, context, cancellationToken)
=> Task.CompletedTask);
});
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Utiliser des transformateurs de documents
Les transformateurs de documents ont accès à un objet de contexte qui inclut :
- Le nom du document en cours de modification.
- ApiDescriptionGroups Associé à ce document.
- IServiceProvider utilisé dans la génération de document.
Les transformateurs de document peuvent également muter le document OpenAPI généré. L’exemple suivant illustre un transformateur de document qui ajoute des informations sur l’API au document OpenAPI.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken) =>
{
document.Info = new()
{
Title = "Checkout API",
Version = "v1",
Description = "API for processing checkouts from cart."
};
return Task.CompletedTask;
});
});
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Les transformateurs de documents activés par le service peuvent utiliser des instances de DI pour modifier l’application. L’exemple suivant illustre un transformateur de document qui utilise le service IAuthenticationSchemeProvider à partir de la couche d’authentification. Il vérifie si des schémas liés au porteur JWT sont inscrits dans l’application et les ajoute au premier niveau du document OpenAPI :
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
var requirements = new Dictionary<string, OpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token"
}
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = requirements;
}
}
}
Les transformateurs de documents sont uniques à l’instance de document à laquelle ils sont associés. Dans l’exemple suivant, un transformateur :
- Inscrit les exigences relatives à l’authentification dans le document
internal
. - Laisse le document
public
non modifié.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi("internal", options =>
{
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
builder.Services.AddOpenApi("public");
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/world", () => "Hello world!")
.WithGroupName("internal");
app.MapGet("/", () => "Hello universe!")
.WithGroupName("public");
app.Run();
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
// Add the security scheme at the document level
var requirements = new Dictionary<string, OpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token"
}
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = requirements;
// Apply it as a requirement for all operations
foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
{
operation.Value.Security.Add(new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }] = Array.Empty<string>()
});
}
}
}
}
Utiliser des transformateurs d’opérations
Les opérations sont des combinaisons uniques de chemins d’accès HTTP et de méthodes dans un document OpenAPI. Les transformateurs d’opérations sont utiles lorsqu’une modification :
- doit être effectuée sur chaque point de terminaison d’une application ;
- ou a été appliquée de manière conditionnelle à certains itinéraires.
Les transformateurs d’opération ont accès à un objet de contexte qui contient :
- Le nom du document auquel appartient l’opération.
- ApiDescription associé à l'opération.
- IServiceProvider utilisé dans la génération de document.
Par exemple, le transformateur d’opération suivant ajoute 500
comme code d’état de réponse pris en charge par toutes les opérations du document.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer((operation, context, cancellationToken) =>
{
operation.Responses.Add("500", new OpenApiResponse { Description = "Internal server error" });
return Task.CompletedTask;
});
});
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Utiliser des transformateurs de schéma
Les schémas sont les modèles de données utilisés dans les corps de requête et de réponse d’un document OpenAPI. Les transformateurs de schéma sont utiles quand une modification :
- Doit être effectuée sur chaque schéma du document, ou
- doit être appliquée avec des conditions à certains schémas.
Les transformateurs de schéma ont accès à un objet de contexte qui contient :
- Le nom du document auquel appartient le schéma.
- Les informations de type JSON associées au schéma cible.
- IServiceProvider utilisé dans la génération de document.
Par exemple, le transformateur de schéma suivant définit le format
des types décimaux sur decimal
à la place de double
:
using Microsoft.AspNetCore.OpenApi;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options => {
// Schema transformer to set the format of decimal to 'decimal'
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(decimal))
{
schema.Format = "decimal";
}
return Task.CompletedTask;
});
});
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => new Body { Amount = 1.1m });
app.Run();
public class Body {
public decimal Amount { get; set; }
}
Ressources supplémentaires
Les API minimales fournissent une prise en charge intégrée de la génération d’informations sur les points de terminaison dans une application via le package Microsoft.AspNetCore.OpenApi
. L’exposition de la définition OpenAPI générée via une interface utilisateur visuelle nécessite un package tiers. Pour plus d’informations sur la prise en charge d’OpenAPI dans les API basées sur un contrôleur, consultez la version .NET 9 de cet article.
Le code suivant est généré par le modèle d’API web minimal ASP.NET Core et utilise OpenAPI :
using Microsoft.AspNetCore.OpenApi;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateTime.Now.AddDays(index),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run();
internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Dans le code en surbrillance précédent :
Microsoft.AspNetCore.OpenApi
est expliqué dans la section suivante.- AddEndpointsApiExplorer : configure l’application afin d’utiliser l’API Explorer pour découvrir et décrire des points de terminaison avec des annotations par défaut.
WithOpenApi
remplace la correspondance, les annotations par défaut générées par l’API Explorer avec celles produites à partir du packageMicrosoft.AspNetCore.OpenApi
. UseSwagger
ajoute l’intergiciel Swagger.- `UseSwaggerUI` active une version incorporée de l’outil de l’interface utilisateur Swagger.
- WithName : le IEndpointNameMetadata sur le point de terminaison est utilisé pour la génération de liens et est traité comme l’ID d’opération dans la spécification OpenAPI du point de terminaison donné.
WithOpenApi
est expliqué plus loin dans cet article.
package NuGet Microsoft.AspNetCore.OpenApi
ASP.NET Core fournit le package Microsoft.AspNetCore.OpenApi
pour interagir avec les spécifications OpenAPI pour les points de terminaison. Le package agit comme un lien entre les modèles OpenAPI définis dans le package Microsoft.AspNetCore.OpenApi
et les points de terminaison définis dans les API minimales. Le package fournit une API qui examine les paramètres, les réponses et les métadonnées d’un point de terminaison pour construire un type d’annotation OpenAPI utilisé pour décrire un point de terminaison.
Microsoft.AspNetCore.OpenApi
est ajouté en tant que PackageReference à un fichier projet :
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.*-*" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
Lors de l’utilisation de Swashbuckle.AspNetCore
avec Microsoft.AspNetCore.OpenApi
, Swashbuckle.AspNetCore
version 6.4.0 ou ultérieure doit être utilisée. Microsoft.OpenApi
version 1.4.3 ou ultérieure doit être utilisée pour tirer parti des constructeurs de copie dans les appels WithOpenApi
.
Ajouter des annotations OpenAPI aux points de terminaison via WithOpenApi
Appeler WithOpenApi
sur le point de terminaison permet l’ajout aux métadonnées du point de terminaison. Ces métadonnées peuvent être :
- Consommées dans des packages tiers comme Swashbuckle.AspNetCore.
- Affichées dans l’interface utilisateur Swagger ou en YAML ou JSON généré pour définir l’API.
app.MapPost("/todoitems/{id}", async (int id, Todo todo, TodoDb db) =>
{
todo.Id = id;
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi();
Modifier l’annotation OpenAPI dans WithOpenApi
La méthode WithOpenApi
accepte une fonction qui peut être utilisée pour modifier l’annotation OpenAPI. Par exemple, dans le code suivant, une description est ajoutée au premier paramètre du point de terminaison :
app.MapPost("/todo2/{id}", async (int id, Todo todo, TodoDb db) =>
{
todo.Id = id;
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi(generatedOperation =>
{
var parameter = generatedOperation.Parameters[0];
parameter.Description = "The ID associated with the created Todo";
return generatedOperation;
});
Ajouter des ID d’opération à OpenAPI
Les ID d’opération sont utilisés pour identifier de manière unique un point de terminaison donné dans OpenAPI. Il est possible d’utiliser la méthode d’extension WithName
pour définir l’ID d’opération utilisé pour une méthode.
app.MapGet("/todoitems2", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithName("GetToDoItems");
Vous pouvez également définir la propriété OperationId
directement sur l’annotation OpenAPI.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
OperationId = "GetTodos"
});
Ajouter des balises à la description OpenAPI
OpenAPI prend en charge l’utilisation d’objets de balise pour catégoriser les opérations. Ces balises sont généralement utilisées pour regrouper des opérations dans l’interface utilisateur de Swagger. Ces balises peuvent être ajoutées à une opération en appelant la méthode d’extension WithTags sur le point de terminaison avec les balises souhaitées.
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithTags("TodoGroup");
Vous pouvez également définir la liste de OpenApiTags
sur l’annotation OpenAPI via la méthode d’extension WithOpenApi
.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Tags = new List<OpenApiTag> { new() { Name = "Todos" } }
});
Ajouter un résumé ou une description du point de terminaison
Le résumé et la description du point de terminaison peuvent être ajoutés en appelant la méthode d’extension WithOpenApi
. Dans le code suivant, les résumés sont définis directement sur l’annotation OpenAPI.
app.MapGet("/todoitems2", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Summary = "This is a summary",
Description = "This is a description"
});
Exclure la description OpenAPI
Dans l’exemple suivant, le point de terminaison /skipme
est exclu et ne peut pas générer une description OpenAPI :
using Microsoft.AspNetCore.OpenApi;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapGet("/swag", () => "Hello Swagger!")
.WithOpenApi();
app.MapGet("/skipme", () => "Skipping Swagger.")
.ExcludeFromDescription();
app.Run();
Marquer une API comme obsolète
Pour marquer un point de terminaison comme obsolète, définissez la propriété Deprecated
sur l’annotation OpenAPI.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Deprecated = true
});
Décrire des types de réponse
OpenAPI prend en charge la description des réponses retournées par une API. Les API minimales prennent en charge trois stratégies pour définir le type de réponse d’un point de terminaison :
- Via la méthode d’extension
Produces
sur le point de terminaison - Via l’attribut
ProducesResponseType
sur le gestionnaire de routage - En retournant
TypedResults
à partir du gestionnaire de routage
Il est possible d’utiliser la méthode d’extension Produces
pour ajouter des métadonnées Produces
à un point de terminaison. Quand aucun paramètre n’est fourni, la méthode d’extension remplit les métadonnées pour le type ciblé sous un code d’état 200
et un type de contenu application/json
.
app
.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
L’utilisation de TypedResults
dans l’implémentation du gestionnaire de routage d’un point de terminaison inclut automatiquement les métadonnées du type de réponse pour le point de terminaison. Par exemple, le code suivant annote automatiquement le point de terminaison avec une réponse sous le code d’état 200
avec un type de contenu application/json
.
app.MapGet("/todos", async (TodoDb db) =>
{
var todos = await db.Todos.ToListAsync());
return TypedResults.Ok(todos);
});
Définir des réponses pour ProblemDetails
Lors de la définition du type de réponse pour les points de terminaison susceptibles de renvoyer une réponse ProblemDetails, les méthodes d’extension ProducesProblem ou ProducesValidationProblem ou TypedResults.Problem
peuvent être utilisées pour ajouter l’annotation appropriée aux métadonnées du point de terminaison. Notez que les méthodes d’extension ProducesProblem
et ProducesValidationProblem
ne peuvent pas être utilisées avec des groupes de gammes dans .NET 8 et versions antérieures .
Lorsqu’aucune annotation explicite n’est fournie par l’une des stratégies ci-dessus, l’infrastructure tente de déterminer un type de réponse par défaut en examinant la signature de la réponse. Cette réponse par défaut est remplie sous le code d’état 200
dans la définition OpenAPI.
Types de réponse multiples
Si un point de terminaison peut retourner différents types de réponse dans différents scénarios, vous pouvez fournir des métadonnées des manières suivantes :
Appeler la méthode d’extension
Produces
plusieurs fois, comme illustré dans l’exemple suivant :app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) => await db.Todos.FindAsync(id) is Todo todo ? Results.Ok(todo) : Results.NotFound()) .Produces<Todo>(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound);
Utiliser
Results<TResult1,TResult2,TResultN>
dans la signature etTypedResults
dans le corps du gestionnaire, comme illustré dans l’exemple suivant :app.MapGet("/book/{id}", Results<Ok<Book>, NotFound> (int id, List<Book> bookList) => { return bookList.FirstOrDefault((i) => i.Id == id) is Book book ? TypedResults.Ok(book) : TypedResults.NotFound(); });
Les
Results<TResult1,TResult2,TResultN>
types d’union déclarent qu’un gestionnaire de routage retourne plusieurs types concrets implémentantIResult
, et tous les types qui implémententIEndpointMetadataProvider
contribueront aux métadonnées du point de terminaison.Les types d’union implémentent des opérateurs de cast implicites. Ces opérateurs permettent au compilateur de convertir automatiquement les types spécifiés dans les arguments génériques en instance du type union. Cette fonctionnalité présente l’avantage supplémentaire de vérifier au moment de la compilation qu’un gestionnaire de routage retourne uniquement les résultats qu’il déclare faire. La tentative de retourner un type qui n’est pas déclaré comme l’un des arguments génériques vers
Results<TResult1,TResult2,TResultN>
entraîne une erreur de compilation.
Décrire le corps et les paramètres de la requête
En plus de décrire les types retournés par un point de terminaison, OpenAPI prend également en charge l’annotation des entrées consommées par une API. Ces entrées se répartissent en deux catégories :
- Paramètres qui apparaissent dans le chemin d’accès, la chaîne de requête, les en-têtes ou les cookies
- Données transmises dans le corps de la requête
L’infrastructure déduit automatiquement les types des paramètres de requête dans le chemin d’accès, la requête et la chaîne d’en-tête en fonction de la signature du gestionnaire de routage.
Pour définir le type d’entrées transmises en tant que corps de la demande, configurez les propriétés à l’aide de la méthode d’extension Accepts
pour définir le type d’objet et le type de contenu attendus par le gestionnaire de requêtes. Dans l’exemple suivant, le point de terminaison accepte un objet Todo
dans le corps de la requête avec un type de contenu attendu de application/xml
.
app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
En plus de la méthode d’extension Accepts
, un type de paramètre peut décrire sa propre annotation en implémentant l’interface IEndpointParameterMetadataProvider
. Par exemple, le type Todo
suivant ajoute une annotation qui nécessite un corps de requête avec un type de contenu application/xml
.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new ConsumesAttribute(typeof(Todo), isOptional: false, "application/xml"));
}
}
Lorsqu’aucune annotation explicite n’est fournie, l’infrastructure tente de déterminer le type de requête par défaut s’il existe un paramètre de corps de la demande dans le gestionnaire de point de terminaison. L’inférence utilise l’heuristique suivante pour produire l’annotation :
- Les paramètres du corps de la requête qui sont lus à partir d’un formulaire via l’attribut
[FromForm]
sont décrits avec le type de contenumultipart/form-data
. - Tous les autres paramètres du corps de la requête sont décrits avec le type de contenu
application/json
. - Le corps de la requête est traité comme facultatif s’il peut avoir la valeur Null ou si la propriété
AllowEmpty
est définie sur l’attributFromBody
.
Contrôle de version de l’API de support
Les API minimales prennent en charge le contrôle de version des API via le package Asp.Versioning.Http. Vous trouverez des exemples de configuration du contrôle de version avec des API minimales dans le référentiel de gestion des versions d’API.
Code source OpenAPI d’ASP.NET Core sur GitHub
Ressources complémentaires
Une application API minimale peut décrire la spécification OpenAPI pour les gestionnaires de routage à l’aide de Swashbuckle.
Pour plus d’informations sur la prise en charge d’OpenAPI dans les API basées sur un contrôleur, consultez la version .NET 9 de cet article.
Le code suivant est une application ASP.NET Core classique avec prise en charge d’OpenAPI :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = builder.Environment.ApplicationName,
Version = "v1" });
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger(); // UseSwaggerUI Protected by if (env.IsDevelopment())
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
$"{builder.Environment.ApplicationName} v1"));
}
app.MapGet("/swag", () => "Hello Swagger!");
app.Run();
Exclure la description OpenAPI
Dans l’exemple suivant, le point de terminaison /skipme
est exclu et ne peut pas générer une description OpenAPI :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(); // UseSwaggerUI Protected by if (env.IsDevelopment())
}
app.MapGet("/swag", () => "Hello Swagger!");
app.MapGet("/skipme", () => "Skipping Swagger.")
.ExcludeFromDescription();
app.Run();
Décrire des types de réponse
L’exemple suivant utilise les types de résultats intégrés pour personnaliser la réponse :
app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound())
.Produces<Todo>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
Ajouter des ID d’opération à OpenAPI
app.MapGet("/todoitems2", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithName("GetToDoItems");
Ajouter des balises à la description OpenAPI
Le code suivant utilise une balise de regroupement OpenAPI :
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithTags("TodoGroup");