Generare documenti OpenAPI
Il Microsoft.AspNetCore.OpenApi
pacchetto fornisce il supporto predefinito per la generazione di documenti OpenAPI in ASP.NET Core. Il pacchetto offre le funzionalità seguenti:
- Supporto per la generazione di documenti OpenAPI in fase di esecuzione e l'accesso tramite un endpoint nell'applicazione.
- Supporto per le API "transformer" che consentono di modificare il documento generato.
- Supporto per la generazione di più documenti OpenAPI da una singola app.
- Sfrutta il supporto dello schema JSON fornito da
System.Text.Json
. - È compatibile con AoT nativo.
Installazione del pacchetto
Installare il pacchetto Microsoft.AspNetCore.OpenApi
:
Eseguire il comando seguente dalla console di Gestione pacchetti:
Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease
Per aggiungere il supporto per la generazione di documenti OpenAPI in fase di compilazione, installare il Microsoft.Extensions.ApiDescription.Server
pacchetto:
Eseguire il comando seguente dalla console di Gestione pacchetti:
Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease
Configurare la generazione di documenti OpenAPI
Il codice seguente:
- Aggiunge servizi OpenAPI.
- Abilita l'endpoint per la visualizzazione del documento OpenAPI in formato JSON.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Avviare l'app e passare a https://localhost:<port>/openapi/v1.json
per visualizzare il documento OpenAPI generato.
Inclusione dei metadati OpenAPI in un'app Web ASP.NET
Inclusione dei metadati OpenAPI per gli endpoint
ASP.NET raccoglie i metadati dagli endpoint dell'app Web e lo usa per generare un documento OpenAPI.
Nelle app basate su controller i metadati vengono raccolti da attributi come [EndpointDescription]
, [HttpPost]
e [Produces]
.
Nelle API minime, i metadati possono essere raccolti dagli attributi, ma possono anche essere impostati usando metodi di estensione e altre strategie, ad esempio la restituzione TypedResults dai gestori di route.
Nella tabella seguente viene fornita una panoramica dei metadati raccolti e delle strategie per impostarlo.
Metadati UFX | Attributo | Metodo di estensione | Altre strategie |
---|---|---|---|
riepilogo | [EndpointSummary] |
WithSummary | |
description | [EndpointDescription] |
WithDescription | |
tag | [Tags] |
WithTags | |
operationId | [EndpointName] |
WithName | |
parameters | [FromQuery] , [FromRoute] , [FromHeader] [FromForm] |
||
descrizione del parametro | [EndpointDescription] |
||
requestBody | [FromBody] |
Accepts | |
responses | [Produces] |
Produces, ProducesProblem | TypedResults |
Esclusione di endpoint | [ExcludeFromDescription] , [ApiExplorerSettings] |
ExcludeFromDescription |
ASP.NET Core non raccoglie metadati dai commenti del documento XML.
Le sezioni seguenti illustrano come includere metadati in un'app per personalizzare il documento OpenAPI generato.
Riepilogo e descrizione
Il riepilogo e la descrizione dell'endpoint possono essere impostati usando gli [EndpointSummary]
attributi e [EndpointDescription]
o nelle API minime, usando i WithSummary metodi di estensione e WithDescription .
L'esempio seguente illustra le diverse strategie per l'impostazione di riepiloghi e descrizioni.
Si noti che gli attributi vengono inseriti nel metodo delegato e non nell'app. Metodo 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!");
tag
OpenAPI supporta la specifica di tag in ogni endpoint sotto forma di categorizzazione.
Nelle API minime, i tag possono essere impostati usando l'attributo [Tags]
o il metodo di WithTags estensione.
L'esempio seguente illustra le diverse strategie per l'impostazione dei tag.
app.MapGet("/extension-methods", () => "Hello world!")
.WithTags("todos", "projects");
app.MapGet("/attributes",
[Tags("todos", "projects")]
() => "Hello world!");
operationId
OpenAPI supporta un operationId in ogni endpoint come identificatore univoco o nome per l'operazione.
Nelle API minime, è possibile impostare operationId usando l'attributo [EndpointName]
o il metodo di WithName estensione.
L'esempio seguente illustra le diverse strategie per impostare operationId.
app.MapGet("/extension-methods", () => "Hello world!")
.WithName("FromExtensionMethods");
app.MapGet("/attributes",
[EndpointName("FromAttributes")]
() => "Hello world!");
parameters
OpenAPI supporta l'annotazione del percorso, della stringa di query, dell'intestazione e cookie dei parametri utilizzati da un'API.
Il framework deduce automaticamente i tipi per i parametri di richiesta in base alla firma del gestore di route.
L'attributo [EndpointDescription]
può essere usato per fornire una descrizione per un parametro.
Nell'esempio seguente viene illustrato come impostare una descrizione per un parametro.
app.MapGet("/attributes",
([Description("This is a description.")] string name) => "Hello world!");
Descrivere il corpo della richiesta
Il requestBody
campo in OpenAPI descrive il corpo di una richiesta che un client API può inviare al server, inclusi i tipi di contenuto supportati e lo schema per il contenuto del corpo.
Quando il metodo del gestore endpoint accetta parametri associati dal corpo della richiesta, ASP.NET Core genera un corrispondente requestBody
per l'operazione nel documento OpenAPI. I metadati per il corpo della richiesta possono essere specificati anche usando attributi o metodi di estensione. È possibile impostare metadati aggiuntivi con un trasformatore di documento o un trasformatore di operazione.
Se l'endpoint non definisce parametri associati al corpo della richiesta, ma utilizza direttamente il corpo della richiesta, ASP.NET Core fornisce meccanismi per specificare i metadati del corpo della HttpContext richiesta. Si tratta di uno scenario comune per gli endpoint che elaborano il corpo della richiesta come flusso.
Alcuni metadati del corpo della richiesta possono essere determinati dai FromBody
parametri o FromForm
del metodo del gestore di route.
Una descrizione per il corpo della richiesta può essere impostata con un [Description]
attributo nel parametro con FromBody
o FromForm
.
Se il FromBody
parametro non è nullable e EmptyBodyBehavior non è impostato su Allow nell'attributo FromBody
, il corpo della richiesta è obbligatorio e il required
campo di requestBody
è impostato su true
nel documento OpenAPI generato.
I corpi del modulo sono sempre obbligatori e sono required
impostati su true
.
Utilizzare un trasformatore di documento o un trasformatore di operazione per impostare i example
campi , examples
o o encoding
per aggiungere estensioni specifiche per il corpo della richiesta nel documento OpenAPI generato.
Altri meccanismi per impostare i metadati del corpo della richiesta dipendono dal tipo di app in fase di sviluppo e sono descritti nelle sezioni seguenti.
I tipi di contenuto per il corpo della richiesta nel documento OpenAPI generato vengono determinati dal tipo del parametro associato al corpo della richiesta o specificato con il Accepts metodo di estensione.
Per impostazione predefinita, il tipo di contenuto di un FromBody
parametro sarà application/json
e il tipo di contenuto per FromForm
i parametri sarà multipart/form-data
o application/x-www-form-urlencoded
.
Il supporto per questi tipi di contenuto predefinito è integrato in API minime e altri tipi di contenuto possono essere gestiti usando l'associazione personalizzata. Per altre informazioni, vedere l'argomento Associazione personalizzata della documentazione sulle API minime.
Esistono diversi modi per specificare un tipo di contenuto diverso per il corpo della richiesta.
Se il tipo del FromBody
parametro implementa IEndpointParameterMetadataProvider, ASP.NET Core usa questa interfaccia per determinare i tipi di contenuto nel corpo della richiesta.
Il framework usa il PopulateMetadata metodo di questa interfaccia per impostare i tipi di contenuto e il tipo del contenuto del corpo del corpo della richiesta. Ad esempio, una Todo
classe che accetta application/xml
o text/xml
content-type può usare IEndpointParameterMetadataProvider per fornire queste informazioni al framework.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo)));
}
}
Il Accepts metodo di estensione può essere usato anche per specificare il tipo di contenuto del corpo della richiesta.
Nell'esempio seguente l'endpoint accetta un Todo
oggetto nel corpo della richiesta con un tipo di contenuto previsto di application/xml
.
app.MapPut("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
Poiché application/xml
non è un tipo di contenuto predefinito, la Todo
classe deve implementare l'interfaccia IBindableFromHttpContext<TSelf> per fornire un'associazione personalizzata per il corpo della richiesta. Ad esempio:
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());
}
Se l'endpoint non definisce parametri associati al corpo della richiesta, usare il Accepts metodo di estensione per specificare il tipo di contenuto accettato dall'endpoint.
Se si specifica <AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts%2A> più volte, vengono usati solo i metadati dell'ultimo, non vengono combinati.
Descrivere i tipi di risposta
OpenAPI supporta la descrizione delle risposte restituite da un'API. ASP.NET Core offre diverse strategie per impostare i metadati di risposta di un endpoint. I metadati di risposta che possono essere impostati includono il codice di stato, il tipo del corpo della risposta e i tipi di contenuto di una risposta. Le risposte in OpenAPI possono avere metadati aggiuntivi, ad esempio descrizione, intestazioni, collegamenti ed esempi. Questi metadati aggiuntivi possono essere impostati con un trasformatore di documento o un trasformatore di operazione.
I meccanismi specifici per l'impostazione dei metadati della risposta dipendono dal tipo di app in fase di sviluppo.
Nelle app per le API minime ASP.NET Core può estrarre i metadati della risposta aggiunti dai metodi di estensione nell'endpoint, gli attributi nel gestore di route e il tipo restituito del gestore di route.
- Il Produces metodo di estensione può essere usato nell'endpoint per specificare il codice di stato, il tipo del corpo della risposta e i tipi di contenuto di una risposta da un endpoint.
- L'attributo
[ProducesResponseType]
o ProducesResponseTypeAttribute<T> può essere usato per specificare il tipo del corpo della risposta. - È possibile usare un gestore di route per restituire un tipo che implementa IEndpointMetadataProvider per specificare il tipo e i tipi di contenuto del corpo della risposta.
- Il ProducesProblem metodo di estensione nell'endpoint può essere usato per specificare il codice di stato e i tipi di contenuto di una risposta di errore.
Si noti che i Produces metodi di estensione e ProducesProblem sono supportati sia RouteHandlerBuilder in che in RouteGroupBuilder. Ciò consente, ad esempio, di definire un set comune di risposte agli errori per tutte le operazioni in un gruppo.
Se non specificato da una delle strategie precedenti, l'oggetto :
- Il codice di stato per la risposta per impostazione predefinita è 200.
- Lo schema per il corpo della risposta può essere dedotto dal tipo restituito implicito o esplicito del metodo endpoint, ad esempio da
T
in Task<TResult>; in caso contrario, viene considerato non specificato. - Il tipo di contenuto per il corpo della risposta specificato o dedotto è "application/json".
Nelle API minime il Produces metodo di estensione e l'attributo [ProducesResponseType]
impostano solo i metadati della risposta per l'endpoint. Non modificano o vincolano il comportamento dell'endpoint, che può restituire un codice di stato o un tipo di corpo della risposta diverso da quello specificato dai metadati e il tipo di contenuto è determinato dal tipo restituito del metodo del gestore di route, indipendentemente da qualsiasi tipo di contenuto specificato negli attributi o nei metodi di estensione.
Il Produces metodo di estensione può specificare il tipo di risposta di un endpoint, con un codice di stato predefinito 200 e un tipo di contenuto predefinito di application/json
. L'esempio seguente illustra questi concetti.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
Può [ProducesResponseType]
essere usato per aggiungere metadati di risposta a un endpoint. Si noti che l'attributo viene applicato al metodo del gestore di route, non alla chiamata al metodo per creare la route, come illustrato nell'esempio seguente:
app.MapGet("/todos",
[ProducesResponseType<List<Todo>>(200)]
async (TodoDb db) => await db.Todos.ToListAsync());
L'uso TypedResults nell'implementazione del gestore di route di un endpoint include automaticamente i metadati del tipo di risposta per l'endpoint. Ad esempio, il codice seguente annota automaticamente l'endpoint con una risposta nel 200
codice di stato con un application/json
tipo di contenuto.
app.MapGet("/todos", async (TodoDb db) =>
{
var todos = await db.Todos.ToListAsync();
return TypedResults.Ok(todos);
});
Solo i tipi restituiti che implementano IEndpointMetadataProvider la creazione di una responses
voce nel documento OpenAPI. Di seguito è riportato un elenco parziale di alcuni dei TypedResults metodi helper che producono una responses
voce:
Metodo helper TypedResults | codice di stato |
---|---|
Ok() | 200 |
Created() | 201 |
CreatedAtRoute() | 201 |
Accepted() | 202 |
AcceptedAtRoute() | 202 |
NoContent() | 204 |
BadRequest() | 400 |
ValidationProblem() | 400 |
NotFound() | 404 |
Conflict() | 409 |
UnprocessableEntity() | 422 |
Tutti questi metodi, ad eccezione NoContent
di , hanno un overload generico che specifica il tipo del corpo della risposta.
È possibile implementare una classe per impostare i metadati dell'endpoint e restituirlo dal gestore di route.
Impostare le risposte per ProblemDetails
Quando si imposta il tipo di risposta per gli endpoint che possono restituire una risposta ProblemDetails, è possibile usare quanto segue per aggiungere i metadati di risposta appropriati per l'endpoint:
- ProducesProblem
- Il metodo di estensione ProducesValidationProblem.
- TypedResults con un codice di stato nell'intervallo (400-499).
Per altre informazioni su come configurare un'app per le API minima per restituire le risposte ProblemDetails, vedere Gestire gli errori nelle API minime.
Più tipi di risposta
Se un endpoint può restituire tipi di risposta diversi in scenari diversi, è possibile fornire metadati nei modi seguenti:
Chiamare il Produces metodo di estensione più volte, come illustrato nell'esempio seguente:
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);
Usare Results<TResult1,TResult2,TResult3,TResult4,TResult5,TResult6> nella firma e TypedResults nel corpo del gestore, come illustrato nell'esempio seguente:
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(); });
I
Results<TResult1,TResult2,TResultN>
tipi di unione dichiarano che un gestore di route restituisce piùIResult
tipi concreti che implementano e uno qualsiasi di questi tipi che implementa IEndpointMetadataProvider contribuirà ai metadati dell'endpoint.I tipi di unione implementano operatori cast impliciti. Questi operatori consentono al compilatore di convertire automaticamente i tipi specificati negli argomenti generici in un'istanza del tipo di unione. Questa funzionalità offre il vantaggio aggiunto di fornire un controllo in fase di compilazione che un gestore di route restituisce solo i risultati che dichiara. Se si tenta di restituire un tipo non dichiarato come uno degli argomenti generici, si
Results<TResult1,TResult2,TResultN>
verifica un errore di compilazione.
Esclusione di endpoint dal documento generato
Per impostazione predefinita, tutti gli endpoint definiti in un'app sono documentati nel file OpenAPI generato, ma gli endpoint possono essere esclusi dal documento usando attributi o metodi di estensione.
Il meccanismo per specificare un endpoint che deve essere escluso dipende dal tipo di app in fase di sviluppo.
Le API minime supportano due strategie per escludere un determinato endpoint dal documento OpenAPI:
- ExcludeFromDescription metodo di estensione
- Attributo
[ExcludeFromDescription]
L'esempio seguente illustra le diverse strategie per escludere un determinato endpoint dal documento OpenAPI generato.
app.MapGet("/extension-method", () => "Hello world!")
.ExcludeFromDescription();
app.MapGet("/attributes",
[ExcludeFromDescription]
() => "Hello world!");
Inclusione dei metadati OpenAPI per i tipi di dati
Le classi o i record C# usati nei corpi di richiesta o risposta vengono rappresentati come schemi nel documento OpenAPI generato. Per impostazione predefinita, solo le proprietà pubbliche sono rappresentate nello schema, ma è JsonSerializerOptions necessario creare anche proprietà dello schema per i campi.
PropertyNamingPolicy Quando è impostato su camel-case (impostazione predefinita in ASP.NET applicazioni Web), i nomi delle proprietà in uno schema sono la forma camel-case della classe o del nome della proprietà del record.
Può [JsonPropertyName]
essere utilizzato in una singola proprietà per specificare il nome della proprietà nello schema.
tipo e formato
La libreria di schemi JSON esegue il mapping dei tipi C# standard a OpenAPI type
e format
come indicato di seguito:
Tipo C# | OpenAPI type |
OpenAPI format |
---|---|---|
int | integer | int32 |
long | integer | int64 |
short | integer | int16 |
byte | integer | uint8 |
float | number | float |
double | number | double |
decimale | number | double |
bool | boolean | |
string | string | |
char | string | char |
byte[] | string | byte |
DateTimeOffset | string | datetime |
DateOnly | string | data |
TimeOnly | string | Ora |
URI | string | uri |
GUID | string | uuid |
oggetto | mancante | |
dynamic | mancante |
Si noti che i tipi dinamico e oggetto non hanno alcun tipo definito in OpenAPI perché possono contenere dati di qualsiasi tipo, inclusi tipi primitivi come int o string.
type
e format
può anche essere impostato con un trasformatore di schema. Ad esempio, è possibile che i format
tipi decimali siano decimal
anziché double
.
Uso degli attributi per aggiungere metadati
ASP.NET usa i metadati degli attributi nelle proprietà della classe o del record per impostare i metadati sulle proprietà corrispondenti dello schema generato.
La tabella seguente riepiloga gli attributi dello System.ComponentModel
spazio dei nomi che forniscono metadati per lo schema generato:
Attributo | Descrizione |
---|---|
[Description] |
Imposta l'oggetto description di una proprietà nello schema. |
[Required] |
Contrassegna una proprietà come required nello schema. |
[DefaultValue] |
Imposta il default valore di una proprietà nello schema. |
[Range] |
Imposta il minimum valore e maximum di un numero intero o numerico. |
[MinLength] |
Imposta l'oggetto minLength di una stringa. |
[MaxLength] |
Imposta l'oggetto maxLength di una stringa. |
[RegularExpression] |
Imposta l'oggetto pattern di una stringa. |
Si noti che nelle app basate su controller questi attributi aggiungono filtri all'operazione per verificare che i dati in ingresso soddisfino i vincoli. Nelle API minime, questi attributi impostano i metadati nello schema generato, ma la convalida deve essere eseguita in modo esplicito tramite un filtro endpoint, nella logica del gestore di route o tramite un pacchetto di terze parti.
Altre origini di metadati per gli schemi generati
Obbligatorio
Le proprietà possono anche essere contrassegnate come required
con il modificatore richiesto .
enum
I tipi di enumerazione in C# sono basati su integer, ma possono essere rappresentati come stringhe in JSON con e [JsonConverter]
.JsonStringEnumConverter Quando un tipo di enumerazione è rappresentato come stringa in JSON, lo schema generato avrà una enum
proprietà con i valori stringa dell'enumerazione.
Un tipo di enumerazione senza un [JsonConverter]
oggetto verrà definito come type: integer
nello schema generato.
Nota: [AllowedValues]
non imposta i enum
valori di una proprietà.
nullable
Le proprietà definite come valore nullable o tipo riferimento hanno nullable: true
nello schema generato. Questo comportamento è coerente con il comportamento predefinito del System.Text.Json deserializzatore, che accetta null
come valore valido per una proprietà nullable.
additionalProperties
Gli schemi vengono generati senza un'asserzione additionalProperties
per impostazione predefinita, che implica l'impostazione predefinita di true
. Questo comportamento è coerente con il comportamento predefinito del System.Text.Json deserializzatore, che ignora automaticamente le proprietà aggiuntive in un oggetto JSON.
Se le proprietà aggiuntive di uno schema devono avere solo valori di un tipo specifico, definire la proprietà o la classe come .Dictionary<string, type>
Il tipo di chiave per il dizionario deve essere string
. Viene generato uno schema con additionalProperties
la specifica dello schema per "type" come tipi di valore obbligatori.
Metadati per i tipi polimorfici
Utilizzare gli [JsonPolymorphic]
attributi e [JsonDerivedType]
in una classe padre per specificare il campo discriminatorio e i sottotipi per un tipo polimorfico.
Aggiunge [JsonDerivedType]
il campo discriminatorio allo schema per ogni sottoclasse, con un'enumerazione che specifica il valore discriminatorio specifico per la sottoclasse. Questo attributo modifica anche il costruttore di ogni classe derivata per impostare il valore discriminatorio.
Una classe astratta con un [JsonPolymorphic]
attributo ha un discriminator
campo nello schema, ma una classe concreta con un [JsonPolymorphic]
attributo non ha un discriminator
campo. OpenAPI richiede che la proprietà discriminatoria sia una proprietà obbligatoria nello schema, ma poiché la proprietà discriminatoria non è definita nella classe base concreta, lo schema non può includere un discriminator
campo.
Aggiunta di metadati con un trasformatore di schema
È possibile usare un trasformatore di schema per eseguire l'override di tutti i metadati predefiniti o aggiungere metadati aggiuntivi, ad esempio example
valori, allo schema generato. Per altre informazioni, vedere Usare trasformatori di schema.
Opzioni per personalizzare la generazione di documenti OpenAPI
Le sezioni seguenti illustrano come personalizzare la generazione di documenti OpenAPI.
Personalizzare il nome del documento OpenAPI
Ogni documento OpenAPI in un'app ha un nome univoco. Il nome del documento predefinito registrato è v1
.
builder.Services.AddOpenApi(); // Document name is v1
Il nome del documento può essere modificato passando il nome come parametro alla AddOpenApi
chiamata.
builder.Services.AddOpenApi("internal"); // Document name is internal
Il nome del documento viene visualizzato in diverse posizioni nell'implementazione OpenAPI.
Quando si recupera il documento OpenAPI generato, il nome del documento viene fornito come argomento del documentName
parametro nella richiesta. Le richieste seguenti risolvono i v1
documenti e internal
.
GET http://localhost:5000/openapi/v1.json
GET http://localhost:5000/openapi/internal.json
Personalizzare la versione OpenAPI di un documento generato
Per impostazione predefinita, la generazione di documenti OpenAPI crea un documento conforme alla versione 3.0 della specifica OpenAPI. Il codice seguente illustra come modificare la versione predefinita del documento OpenAPI:
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
});
Personalizzare la route dell'endpoint OpenAPI
Per impostazione predefinita, l'endpoint OpenAPI registrato tramite una chiamata a MapOpenApi espone il documento all'endpoint /openapi/{documentName}.json
. Il codice seguente illustra come personalizzare la route in cui è registrato il documento OpenAPI:
app.MapOpenApi("/openapi/{documentName}/openapi.json");
È possibile, ma non consigliato, rimuovere il documentName
parametro di route dalla route dell'endpoint. Quando il documentName
parametro di route viene rimosso dalla route dell'endpoint, il framework tenta di risolvere il nome del documento dal parametro di query. La mancata fornitura documentName
di nella route o nella query può comportare un comportamento imprevisto.
Personalizzare l'endpoint OpenAPI
Poiché il documento OpenAPI viene fornito tramite un endpoint del gestore di route, qualsiasi personalizzazione disponibile per gli endpoint minimi standard è disponibile per l'endpoint OpenAPI.
Limitare l'accesso ai documenti OpenAPI agli utenti autorizzati
L'endpoint OpenAPI non abilita i controlli di autorizzazione per impostazione predefinita. Tuttavia, i controlli di autorizzazione possono essere applicati al documento OpenAPI. Nel codice seguente, l'accesso al documento OpenAPI è limitato a quelli con il tester
ruolo :
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();
Documento OpenAPI generato nella cache
Il documento OpenAPI viene rigenerato ogni volta che viene inviata una richiesta all'endpoint OpenAPI. La rigenerazione consente ai trasformatori di incorporare lo stato dell'applicazione dinamica nel loro funzionamento. Ad esempio, rigenerando una richiesta con i dettagli del contesto HTTP. Se applicabile, il documento OpenAPI può essere memorizzato nella cache per evitare di eseguire la pipeline di generazione di documenti in ogni richiesta 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();
Trasformatori di documento OpenAPI
Questa sezione illustra come personalizzare i documenti OpenAPI con trasformatori.
Personalizzare documenti OpenAPI con trasformatori
I trasformatori forniscono un'API per la modifica del documento OpenAPI con personalizzazioni definite dall'utente. I trasformatori sono utili per scenari come:
- Aggiunta di parametri a tutte le operazioni in un documento.
- Modifica delle descrizioni per parametri o operazioni.
- Aggiunta di informazioni di primo livello al documento OpenAPI.
I trasformatori rientrano in tre categorie:
- I trasformatori di documento hanno accesso all'intero documento OpenAPI. Questi possono essere usati per apportare modifiche globali al documento.
- I trasformatori di operazione si applicano a ogni singola operazione. Ogni singola operazione è una combinazione di percorso e metodo HTTP. Possono essere usati per modificare parametri o risposte sugli endpoint.
- I trasformatori di schema si applicano a ogni schema del documento. Questi possono essere usati per modificare lo schema dei corpi di richiesta o di risposta o per qualsiasi schema annidato.
I trasformatori possono essere registrati nel documento chiamando il AddDocumentTransformer metodo sull'oggetto OpenApiOptions . Il frammento di codice seguente illustra diversi modi per registrare i trasformatori nel documento:
- Registrare un trasformatore di documento usando un delegato.
- Registrare un trasformatore di documento usando un'istanza di IOpenApiDocumentTransformer.
- Registrare un trasformatore di documento usando un'attivazione IOpenApiDocumentTransformerdell'inserimento delle dipendenze.
- Registrare un trasformatore di operazione usando un delegato.
- Registrare un trasformatore di operazione usando un'istanza di IOpenApiOperationTransformer.
- Registrare un trasformatore di operazione utilizzando un'eccezione attivata dall'inserimento IOpenApiOperationTransformerdelle dipendenze.
- Registrare un trasformatore di schema usando un delegato.
- Registrare un trasformatore di schema usando un'istanza di IOpenApiSchemaTransformer.
- Registrare un trasformatore di schema usando un'attivazione IOpenApiSchemaTransformerdell'inserimento delle dipendenze.
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();
Ordine di esecuzione per trasformatori
I trasformatori sono eseguiti nell'ordine first-in first-out in base alla registrazione. Nel frammento di codice seguente, il trasformatore di documento ha accesso alle modifiche apportate dal trasformatore di operazione:
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();
Usare trasformatori di documento
I trasformatori di documento hanno accesso a un oggetto contesto che include:
- Nome del documento da modificare.
- Oggetto ApiDescriptionGroups associato a tale documento.
- Oggetto IServiceProvider utilizzato nella generazione di documenti.
I trasformatori di documento possono anche modificare il documento OpenAPI generato. L'esempio seguente illustra un trasformatore di documento che aggiunge alcune informazioni sull'API al documento 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();
I trasformatori di documento attivati dal servizio possono usare istanze di inserimento delle dipendenze per modificare l'app. L'esempio seguente illustra un trasformatore di documento che usa il IAuthenticationSchemeProvider servizio dal livello di autenticazione. Controlla se gli schemi correlati al bearer JWT sono registrati nell'app e li aggiunge al livello principale del documento 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;
}
}
}
I trasformatori di documento sono univoci per l'istanza del documento a cui sono associati. Nell'esempio seguente un trasformatore:
- Registra i requisiti correlati all'autenticazione nel
internal
documento. - Lascia invariato il
public
documento.
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>()
});
}
}
}
}
Usare trasformatori di operazione
Le operazioni sono combinazioni univoche di percorsi e metodi HTTP in un documento OpenAPI. I trasformatori di operazione sono utili quando si modifica:
- Deve essere eseguito a ogni endpoint in un'app o
- Applicato in modo condizionale a determinate route.
I trasformatori di operazione hanno accesso a un oggetto contesto che contiene:
- Nome del documento a cui appartiene l'operazione.
- Classe ApiDescription associata all'operazione.
- Oggetto IServiceProvider utilizzato nella generazione di documenti.
Ad esempio, il trasformatore di operazione seguente aggiunge 500
come codice di stato della risposta supportato da tutte le operazioni nel documento.
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();
Usare trasformatori di schema
Gli schemi sono i modelli di dati usati nei corpi di richiesta e risposta in un documento OpenAPI. I trasformatori di schema sono utili quando una modifica:
- Deve essere eseguito su ogni schema nel documento o
- Applicato in modo condizionale a determinati schemi.
I trasformatori di schema hanno accesso a un oggetto contesto che contiene:
- Nome del documento a cui appartiene lo schema.
- Informazioni sul tipo JSON associate allo schema di destinazione.
- Oggetto IServiceProvider utilizzato nella generazione di documenti.
Ad esempio, il trasformatore di schema seguente imposta l'oggetto format
dei tipi decimali decimal
su anziché double
su :
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; }
}
Risorse aggiuntive
Le API minime offrono il supporto predefinito per la generazione di informazioni sugli endpoint in un'app tramite il Microsoft.AspNetCore.OpenApi
pacchetto. L'esposizione della definizione OpenAPI generata tramite un'interfaccia utente visiva richiede un pacchetto di terze parti. Per informazioni sul supporto per OpenAPI nelle API basate su controller, vedere la versione .NET 9 di questo articolo.
Il codice seguente viene generato dal modello api Web minimo di ASP.NET core e usa 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);
}
Nel codice evidenziato precedente:
Microsoft.AspNetCore.OpenApi
viene spiegato nella sezione successiva.- AddEndpointsApiExplorer : configura l'app per l'uso di Esplora API per individuare e descrivere gli endpoint con annotazioni predefinite.
WithOpenApi
esegue l'override delle annotazioni predefinite generate da Esplora API con quelle generate dalMicrosoft.AspNetCore.OpenApi
pacchetto. UseSwagger
aggiunge il middleware Swagger.- 'UseSwaggerUI' abilita una versione incorporata dello strumento dell'interfaccia utente di Swagger.
- WithNameIEndpointNameMetadata: nell'endpoint viene usato per la generazione di collegamenti e viene considerato come l'ID operazione nella specifica OpenAPI dell'endpoint specificato.
WithOpenApi
viene spiegato più avanti in questo articolo.
pacchetto NuGet Microsoft.AspNetCore.OpenApi
ASP.NET Core fornisce il Microsoft.AspNetCore.OpenApi
pacchetto per interagire con le specifiche OpenAPI per gli endpoint. Il pacchetto funge da collegamento tra i modelli OpenAPI definiti nel Microsoft.AspNetCore.OpenApi
pacchetto e gli endpoint definiti nelle API minime. Il pacchetto fornisce un'API che esamina i parametri, le risposte e i metadati di un endpoint per costruire un tipo di annotazione OpenAPI usato per descrivere un endpoint.
Microsoft.AspNetCore.OpenApi
viene aggiunto come PackageReference a un file di progetto:
<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>
Quando si usa Swashbuckle.AspNetCore
con Microsoft.AspNetCore.OpenApi
, è necessario usare la Swashbuckle.AspNetCore
versione 6.4.0 o successiva. Microsoft.OpenApi
1.4.3 o versione successiva deve essere usato per sfruttare i costruttori di copia nelle WithOpenApi
chiamate.
Aggiungere annotazioni OpenAPI agli endpoint tramite WithOpenApi
La chiamata WithOpenApi
all'endpoint aggiunge ai metadati dell'endpoint. Questi metadati possono essere:
- Utilizzato in pacchetti di terze parti come Swashbuckle.AspNetCore.
- Visualizzato nell'interfaccia utente di Swagger o in YAML o JSON generato per definire 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();
Modificare l'annotazione OpenAPI in WithOpenApi
Il WithOpenApi
metodo accetta una funzione che può essere usata per modificare l'annotazione OpenAPI. Nel codice seguente, ad esempio, viene aggiunta una descrizione al primo parametro dell'endpoint:
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;
});
Aggiungere ID operazione a OpenAPI
Gli ID operazione vengono usati per identificare in modo univoco un determinato endpoint in OpenAPI. Il WithName
metodo di estensione può essere usato per impostare l'ID operazione utilizzato per un metodo.
app.MapGet("/todoitems2", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithName("GetToDoItems");
In alternativa, la OperationId
proprietà può essere impostata direttamente nell'annotazione OpenAPI.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
OperationId = "GetTodos"
});
Aggiungere tag alla descrizione OpenAPI
OpenAPI supporta l'uso di oggetti tag per classificare le operazioni. Questi tag vengono in genere usati per raggruppare le operazioni nell'interfaccia utente di Swagger. Questi tag possono essere aggiunti a un'operazione richiamando il metodo di estensione WithTags nell'endpoint con i tag desiderati.
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithTags("TodoGroup");
In alternativa, l'elenco di OpenApiTags
può essere impostato nell'annotazione OpenAPI tramite il WithOpenApi
metodo di estensione.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Tags = new List<OpenApiTag> { new() { Name = "Todos" } }
});
Aggiungere il riepilogo o la descrizione dell'endpoint
Il riepilogo e la descrizione dell'endpoint possono essere aggiunti richiamando il WithOpenApi
metodo di estensione. Nel codice seguente i riepiloghi vengono impostati direttamente nell'annotazione 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"
});
Escludere la descrizione OpenAPI
Nell'esempio seguente l'endpoint /skipme
viene escluso dalla generazione di una descrizione 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();
Contrassegnare un'API come obsoleta
Per contrassegnare un endpoint come obsoleto, impostare la proprietà nell'annotazione Deprecated
OpenAPI.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Deprecated = true
});
Descrivere i tipi di risposta
OpenAPI supporta la descrizione delle risposte restituite da un'API. Le API minime supportano tre strategie per impostare il tipo di risposta di un endpoint:
- Tramite il metodo di
Produces
estensione nell'endpoint - Tramite l'attributo
ProducesResponseType
nel gestore di route - Restituendo
TypedResults
dal gestore di route
Il Produces
metodo di estensione può essere usato per aggiungere Produces
metadati a un endpoint. Quando non vengono forniti parametri, il metodo di estensione popola i metadati per il tipo di destinazione con un 200
codice di stato e un application/json
tipo di contenuto.
app
.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
L'uso TypedResults
nell'implementazione del gestore di route di un endpoint include automaticamente i metadati del tipo di risposta per l'endpoint. Ad esempio, il codice seguente annota automaticamente l'endpoint con una risposta nel 200
codice di stato con un application/json
tipo di contenuto.
app.MapGet("/todos", async (TodoDb db) =>
{
var todos = await db.Todos.ToListAsync());
return TypedResults.Ok(todos);
});
Impostare le risposte per ProblemDetails
Quando si imposta il tipo di risposta per gli endpoint che possono restituire una risposta ProblemDetails, il ProducesProblem metodo di estensione, ProducesValidationProblemo è TypedResults.Problem
possibile usare per aggiungere l'annotazione appropriata ai metadati dell'endpoint. Si noti che i ProducesProblem
metodi di estensione e ProducesValidationProblem
non possono essere usati con i gruppi di route in .NET 8 e versioni precedenti.
Quando non sono presenti annotazioni esplicite fornite da una delle strategie precedenti, il framework tenta di determinare un tipo di risposta predefinito esaminando la firma della risposta. Questa risposta predefinita viene popolata sotto il 200
codice di stato nella definizione OpenAPI.
Più tipi di risposta
Se un endpoint può restituire tipi di risposta diversi in scenari diversi, è possibile fornire metadati nei modi seguenti:
Chiamare il
Produces
metodo di estensione più volte, come illustrato nell'esempio seguente: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);
Usare
Results<TResult1,TResult2,TResultN>
nella firma eTypedResults
nel corpo del gestore, come illustrato nell'esempio seguente: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(); });
I
Results<TResult1,TResult2,TResultN>
tipi di unione dichiarano che un gestore di route restituisce piùIResult
tipi concreti che implementano e uno qualsiasi di questi tipi che implementaIEndpointMetadataProvider
contribuirà ai metadati dell'endpoint.I tipi di unione implementano operatori cast impliciti. Questi operatori consentono al compilatore di convertire automaticamente i tipi specificati negli argomenti generici in un'istanza del tipo di unione. Questa funzionalità offre il vantaggio aggiunto di fornire un controllo in fase di compilazione che un gestore di route restituisce solo i risultati che dichiara. Se si tenta di restituire un tipo non dichiarato come uno degli argomenti generici, si
Results<TResult1,TResult2,TResultN>
verifica un errore di compilazione.
Descrivere il corpo e i parametri della richiesta
Oltre a descrivere i tipi restituiti da un endpoint, OpenAPI supporta anche l'annotazione degli input utilizzati da un'API. Questi input rientrano in due categorie:
- Parametri visualizzati nel percorso, nella stringa di query, nelle intestazioni o nei cookie
- Dati trasmessi come parte del corpo della richiesta
Il framework deduce automaticamente i tipi per i parametri della richiesta nel percorso, nella query e nella stringa di intestazione in base alla firma del gestore di route.
Per definire il tipo di input trasmesso come corpo della richiesta, configurare le proprietà usando il Accepts
metodo di estensione per definire il tipo di oggetto e il tipo di contenuto previsti dal gestore della richiesta. Nell'esempio seguente l'endpoint accetta un Todo
oggetto nel corpo della richiesta con un tipo di contenuto previsto di application/xml
.
app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
Oltre al Accepts
metodo di estensione, un tipo di parametro può descrivere la propria annotazione implementando l'interfaccia IEndpointParameterMetadataProvider
. Ad esempio, il tipo seguente Todo
aggiunge un'annotazione che richiede un corpo della richiesta con un application/xml
tipo di contenuto.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new ConsumesAttribute(typeof(Todo), isOptional: false, "application/xml"));
}
}
Quando non viene fornita alcuna annotazione esplicita, il framework tenta di determinare il tipo di richiesta predefinito se è presente un parametro del corpo della richiesta nel gestore dell'endpoint. L'inferenza usa le euristiche seguenti per produrre l'annotazione:
- I parametri del corpo della richiesta letti da un modulo tramite l'attributo
[FromForm]
vengono descritti con ilmultipart/form-data
tipo di contenuto. - Tutti gli altri parametri del corpo della richiesta sono descritti con il
application/json
tipo di contenuto. - Il corpo della richiesta viene considerato facoltativo se è nullable o se la
AllowEmpty
proprietà è impostata sull'attributoFromBody
.
Supporto del controllo delle versioni delle API
Le API minime supportano il controllo delle versioni delle API tramite il pacchetto Asp.Versioning.Http. Esempi di configurazione del controllo delle versioni con API minime sono disponibili nel repository di controllo delle versioni dell'API.
ASP.NET codice sorgente OpenAPI core in GitHub
Risorse aggiuntive
Un'app per le API minima può descrivere la specifica OpenAPI per i gestori di route usando Swashbuckle.
Per informazioni sul supporto per OpenAPI nelle API basate su controller, vedere la versione .NET 9 di questo articolo.
Il codice seguente è un'app ASP.NET Core tipica con supporto 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();
Escludere la descrizione OpenAPI
Nell'esempio seguente l'endpoint /skipme
viene escluso dalla generazione di una descrizione 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();
Descrivere i tipi di risposta
Nell'esempio seguente vengono usati i tipi di risultati predefiniti per personalizzare la risposta:
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);
Aggiungere id operazione a OpenAPI
app.MapGet("/todoitems2", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithName("GetToDoItems");
Aggiungere tag alla descrizione OpenAPI
Il codice seguente usa un tag di raggruppamento OpenAPI:
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithTags("TodoGroup");