Compartir vía


Generación de documentos de OpenAPI

El paquete Microsoft.AspNetCore.OpenApi proporciona compatibilidad integrada con la generación de documentos de OpenAPI en ASP.NET Core. El paquete proporciona las características siguientes:

  • Compatibilidad con la generación de documentos de OpenAPI en tiempo de ejecución y acceso a ellos a través de un punto de conexión en la aplicación.
  • Compatibilidad con las API "transformadoras" que permiten modificar el documento generado.
  • Compatibilidad con la generación de varios documentos OpenAPI desde una sola aplicación.
  • Aprovecha la compatibilidad de esquemas JSON proporcionada por System.Text.Json.
  • Es compatible con AoT nativo.

Instalación del paquete

Instala el paquete Microsoft.AspNetCore.OpenApi:

Ejecuta el siguiente comando desde la Consola del Administrador de paquetes:

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

Configuración de la generación de documentos de OpenAPI

El código siguiente:

  • Agrega servicios de OpenAPI.
  • Habilita el punto de conexión para ver el documento OpenAPI en formato JSON.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

Inicie la aplicación y vaya a https://localhost:<port>/openapi/v1.json para ver el documento de OpenAPI generado.

Opciones para personalizar la generación de documentos de OpenAPI

En las secciones siguientes, se muestra cómo personalizar la generación de documentos de OpenAPI.

Personalización del nombre del documento de OpenAPI

Cada documento de OpenAPI en una aplicación tiene un nombre único. El nombre de documento predeterminado que está registrado es v1.

builder.Services.AddOpenApi(); // Document name is v1

El nombre del documento se puede modificar pasando el nombre como parámetro a la llamada AddOpenApi.

builder.Services.AddOpenApi("internal"); // Document name is internal

El nombre del documento aparece en varios lugares de la implementación de OpenAPI.

Al capturar el documento de OpenAPI generado, el nombre del documento se proporciona como argumento de parámetro documentName en la solicitud. Las siguientes solicitudes resuelven los documentos v1 y internal.

GET http://localhost:5000/openapi/v1.json
GET http://localhost:5000/openapi/internal.json

Personalización de la versión de OpenAPI de un documento generado

De forma predeterminada, la generación de documentos de OpenAPI crea un documento compatible con v3.0 de la especificación OpenAPI. En el código siguiente se muestra cómo modificar la versión predeterminada del documento de OpenAPI:

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
});

Personalización de la ruta del punto de conexión de OpenAPI

De forma predeterminada, el punto de conexión de OpenAPI registrado a través de una llamada a MapOpenApi expone el documento en el punto de conexión de /openapi/{documentName}.json. En el código siguiente se muestra cómo personalizar la ruta en la que se registra el documento de OpenAPI:

app.MapOpenApi("/openapi/{documentName}/openapi.json");

Es posible, pero no se recomienda, quitar el parámetro de ruta documentName de la ruta del punto de conexión. Cuando se quita el parámetro de ruta documentName de la ruta del punto de conexión, el marco intenta resolver el nombre del documento del parámetro de consulta. No proporcionar documentName en la ruta o la consulta puede dar lugar a un comportamiento inesperado.

Personalización del punto de conexión de OpenAPI

Dado que el documento OpenAPI se sirve a través de un punto de conexión de controlador de ruta, cualquier personalización disponible para los puntos de conexión mínimos estándar está disponible para el punto de conexión de OpenAPI.

Limitación del acceso al documento de OpenAPI a usuarios autorizados

El punto de conexión de OpenAPI no habilita ninguna comprobación de autorización de forma predeterminada. Sin embargo, las comprobaciones de autorización se pueden aplicar al documento de OpenAPI. En el código siguiente, el acceso al documento de OpenAPI se limita a los que tienen el rol de 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();

Documento de OpenAPI generado por caché

El documento de OpenAPI se vuelve a generar cada vez que se envía una solicitud al punto de conexión de OpenAPI. La regeneración permite a los transformadores incorporar el estado dinámico de la aplicación en su funcionamiento. Por ejemplo, regeneración de una solicitud con detalles del contexto HTTP. Si procede, el documento de OpenAPI se puede almacenar en caché para evitar ejecutar la canalización de generación del documento en cada solicitud 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();

Generación de documentos de OpenAPI en tiempo de compilación

En las aplicaciones web típicas, los documentos openAPI se generan en tiempo de ejecución y se sirven a través de una solicitud HTTP al servidor de aplicaciones.

En algunos escenarios, resulta útil generar el documento openAPI durante el paso de compilación de la aplicación. Entre los escenarios se incluyen los siguientes:

  • Generación de documentación de OpenAPI que se confirma en el control de código fuente.
  • Generación de documentación de OpenAPI que se usa para las pruebas de integración basadas en especificaciones.
  • Generación de documentación de OpenAPI que se sirve estáticamente desde el servidor web.

Para agregar compatibilidad con la generación de documentos OpenAPI en tiempo de compilación, instala el paquete Microsoft.Extensions.ApiDescription.Server:

Ejecuta el siguiente comando desde la Consola del Administrador de paquetes:

Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease

Tras la instalación, este paquete generará automáticamente los documentos de Open API asociados a la aplicación durante la compilación y los rellenará en el directorio de salida de la aplicación.

$ dotnet build
$ cat bin/Debug/net9.0/{ProjectName}.json

Personalización de la generación de documentos en tiempo de compilación

Modificación del directorio de salida del archivo de Open API generado

De forma predeterminada, el documento OpenAPI generado se emitirá en el directorio de salida de la aplicación. Para modificar la ubicación del archivo emitido, establezca la ruta de acceso de destino en la OpenApiDocumentsDirectory propiedad .

<PropertyGroup>
  <OpenApiDocumentsDirectory>./</OpenApiDocumentsDirectory>
</PropertyGroup>

El valor de OpenApiDocumentsDirectory se resuelve en relación con el archivo del proyecto. El uso del ./ valor anterior emitirá el documento openAPI en el mismo directorio que el archivo del proyecto.

Modificación del nombre del archivo de salida

De forma predeterminada, el documento OpenAPI generado tendrá el mismo nombre que el archivo de proyecto de la aplicación. Para modificar el nombre del archivo emitido, establezca el --file-name argumento en la OpenApiGenerateDocumentsOptions propiedad .

<PropertyGroup>
  <OpenApiGenerateDocumentsOptions>--file-name my-open-api</OpenApiGenerateDocumentsOptions>
</PropertyGroup>

Selección del documento openAPI que se va a generar

Algunas aplicaciones se pueden configurar para emitir varios documentos openAPI, para varias versiones de una API o para distinguir entre las API públicas e internas. De forma predeterminada, el generador de documentos en tiempo de compilación emitirá archivos para todos los documentos configurados en una aplicación. Para emitir solo un nombre de documento único, establezca el --document-name argumento en la OpenApiGenerateDocumentsOptions propiedad .

<PropertyGroup>
  <OpenApiGenerateDocumentsOptions>--document-name v2</OpenApiGenerateDocumentsOptions>
</PropertyGroup>

Personalización del comportamiento en tiempo de ejecución durante la generación de documentos en tiempo de compilación

En segundo plano, las funciones de generación de documentos openAPI en tiempo de compilación inician el punto de entrada de la aplicación con una implementación de servidor inert. Se trata de un requisito para generar documentos openAPI precisos, ya que no se puede analizar estáticamente toda la información del documento de OpenAPI. Dado que se invoca el punto de entrada de la aplicación, se invocará cualquier lógica en el inicio de las aplicaciones. Esto incluye código que inserta servicios en el contenedor de inserción de dependencias o lee de la configuración. En algunos escenarios, es necesario restringir las rutas de código que se ejecutarán cuando se invoque el punto de entrada de la aplicación desde la generación de documentos en tiempo de compilación. Entre los escenarios se incluyen los siguientes:

  • No se leen determinadas cadenas de configuración.
  • No registrar servicios relacionados con la base de datos.

Para restringir que la canalización de generación en tiempo de compilación invoque estas rutas de código, pueden estar condicionadas detrás de una comprobación del ensamblado de entrada de la siguiente manera:

using System.Reflection;

var builder = WebApplication.CreateBuilder();

if (Assembly.GetEntryAssembly()?.GetName().Name != "GetDocument.Insider")
{
  builder.Services.AddDefaults();
}

Las API mínimas proporcionan compatibilidad integrada para generar información sobre los puntos de conexión de una aplicación mediante el paquete Microsoft.AspNetCore.OpenApi. La exposición de la definición de OpenAPI generada a través de una interfaz de usuario visual requiere un paquete de terceros. Para obtener información sobre la compatibilidad con OpenAPI en API basadas en controladores, consulta la versión .NET 9 de este artículo.

El siguiente código se genera mediante la plantilla de API web mínima de ASP.NET Core y 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);
}

En el código resaltado anterior:

  • Microsoft.AspNetCore.OpenApi se explica en la sección siguiente.
  • AddEndpointsApiExplorer: configura la aplicación para usar el Explorador de API para detectar y describir puntos de conexión con anotaciones predeterminadas. WithOpenApi reemplaza las anotaciones coincidentes predeterminadas generadas por el Explorador de API por las generadas desde el paquete Microsoft.AspNetCore.OpenApi.
  • UseSwagger agrega el middleware de Swagger.
  • "UseSwaggerUI" habilita una versión insertada de la herramienta de interfaz de usuario de Swagger.
  • WithName: el objeto IEndpointNameMetadata del punto de conexión se usa para la generación de vínculos y se trata como identificador de operación en la especificación de OpenAPI del punto de conexión dado.
  • WithOpenApi se explica posteriormente en este artículo.

Paquete NuGet Microsoft.AspNetCore.OpenApi

ASP.NET Core proporciona el paquete Microsoft.AspNetCore.OpenApi para interactuar con las especificaciones de OpenAPI de los puntos de conexión. El paquete actúa como vínculo entre los modelos de OpenAPI definidos en el paquete Microsoft.AspNetCore.OpenApi y los puntos de conexión definidos en las API mínimas. El paquete proporciona una API que examina los parámetros, las respuestas y los metadatos de un punto de conexión para construir un tipo de anotación de OpenAPI que se usa para describir un punto de conexión.

Microsoft.AspNetCore.OpenApi se agrega como PackageReference a un archivo de proyecto:

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

Cuando se usa Swashbuckle.AspNetCore con Microsoft.AspNetCore.OpenApi, se debe usar Swashbuckle.AspNetCore 6.4.0 y versiones posteriores. Se debe usar Microsoft.OpenApi 1.4.3 o versiones posteriores para aprovechar los constructores de copias en las invocaciones de WithOpenApi.

Adición de anotaciones de OpenAPI a los puntos de conexión mediante WithOpenApi

La llamada a WithOpenApi en el punto de conexión se agrega a los metadatos del punto de conexión. Estos metadatos presentan las siguientes características:

  • Se pueden consumir en paquetes de terceros, como Swashbuckle.AspNetCore.
  • Se muestran en la interfaz de usuario de Swagger o en el código YAML o JSON generado para definir la 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();

Modificación de la anotación de OpenAPI en WithOpenApi

El método WithOpenApi acepta una función que se puede usar para modificar la anotación de OpenAPI. Por ejemplo, en el código siguiente, se agrega una descripción al primer parámetro del punto de conexión:

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;
});

Adición de identificadores de operación a OpenAPI

Los identificadores de operación se usan para identificar de forma única un punto de conexión determinado en OpenAPI. El método de extensión WithName se puede usar para establecer el identificador de operación utilizado para un método.

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

Como alternativa, puede establecerse directamente la propiedad OperationId en la anotación de OpenAPI.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        OperationId = "GetTodos"
    });

Adición de etiquetas a la descripción de OpenAPI

OpenAPI admite el uso de objetos de etiqueta para clasificar las operaciones. Estas etiquetas se suelen usar para agrupar operaciones en la interfaz de usuario de Swagger. Las etiquetas se pueden agregar a una operación mediante la invocación del método de extensión WithTags en el punto de conexión con las etiquetas deseadas.

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");

Como alternativa, se puede establecer la lista de OpenApiTags en la anotación de OpenAPI mediante el método de extensión WithOpenApi.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Tags = new List<OpenApiTag> { new() { Name = "Todos" } }
    });

Agregar resumen o descripción del punto de conexión

El resumen y la descripción del punto de conexión se pueden agregar mediante la invocación del método de extensiónWithOpenApi. En el código siguiente, los resúmenes se establecen directamente en la anotación de 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"
    });

Exclusión de la descripción de OpenAPI

En el ejemplo siguiente, el punto de conexión /skipme se excluye de la generación de una descripción de 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();

Marcado de una API como obsoleta

Para marcar un punto de conexión como obsoleto, establece la propiedad Deprecated en la anotación de OpenAPI.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Deprecated = true
    });

Describir los tipos de respuesta

OpenAPI permite proporcionar una descripción de las respuestas que devuelve una API. Las API mínimas admiten tres estrategias para establecer el tipo de respuesta de un punto de conexión:

  • Mediante el método de extensión Produces en el punto de conexión
  • Mediante el atributo ProducesResponseType en el controlador de ruta
  • Mediante la devolución de TypedResults desde el controlador de ruta

El método de extensión Produces se puede usar para agregar metadatos de Produces a un punto de conexión. Cuando no se proporciona ningún parámetro, el método de extensión rellena los metadatos del tipo de destino bajo un código de estado 200 y un tipo de contenido application/json.

app
    .MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .Produces<IList<Todo>>();

Al usar TypedResults en la implementación del controlador de ruta de un punto de conexión, se incluyen automáticamente los metadatos del tipo de respuesta para dicho punto. Por ejemplo, el código siguiente anota automáticamente una respuesta en el punto de conexión bajo el código de estado 200 con un tipo de contenido application/json.

app.MapGet("/todos", async (TodoDb db) =>
{
    var todos = await db.Todos.ToListAsync());
    return TypedResults.Ok(todos);
});

Establecimiento de respuestas para ProblemDetails

Al establecer el tipo de respuesta para los puntos de conexión que pueden devolver una respuesta ProblemDetails, se puede usar el método de extensión ProducesProblem, ProducesValidationProblem o TypedResults.Problem para agregar la anotación adecuada a los metadatos del punto de conexión. Ten en cuenta que los métodos de extensión ProducesProblem y ProducesValidationProblem no se pueden usar con los grupos de rutas en .NET 8 y versiones anteriores.

Cuando las estrategias anteriores no proporcionan anotaciones explícitas, el marco intenta determinar un tipo de respuesta predeterminado mediante el examen de la signatura de la respuesta. Esta respuesta predeterminada se rellena bajo el código de estado 200 en la definición de OpenAPI.

Tipos de respuestas múltiples

Si un punto de conexión puede devolver diferentes tipos de respuesta en escenarios distintos, puedes proporcionar metadatos de las siguientes maneras:

  • Llama al método de extensión Produces varias veces, como se muestra en el ejemplo siguiente:

    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);
    
  • Usa Results<TResult1,TResult2,TResultN> en la firma y TypedResults en el cuerpo del controlador, como se muestra en el ejemplo siguiente:

    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();
    });
    

    Los tipos de unión Results<TResult1,TResult2,TResultN> declaran que un controlador de ruta devuelve varios tipos de elementos IResult que implementan tipos concretos, y cualquiera de esos tipos que implementa IEndpointMetadataProvider contribuirá a los metadatos del punto de conexión.

    Los tipos de unión implementan operadores de conversión implícitos. Estos operadores habilitan el compilador para convertir automáticamente los tipos especificados en los argumentos genéricos en una instancia del tipo de unión. Esto tiene la ventaja adicional de proporcionar la comprobación en tiempo de compilación de que un controlador de ruta solo devuelve los resultados que sí declara. Si se intenta devolver un tipo que no se declara como uno de los argumentos genéricos de Results<TResult1,TResult2,TResultN>, se producirá un error de compilación.

Descripción de los parámetros y el cuerpo de la solicitud

Además de describir los tipos devueltos por un punto de conexión, OpenAPI también admite la anotación de las entradas que consume una API. Estas entradas se dividen en dos categorías:

  • Parámetros que aparecen en la ruta de acceso, cadena de consulta, encabezados o cookies
  • Datos transmitidos como parte del cuerpo de la solicitud

El marco deduce automáticamente los tipos de parámetros de solicitud en la cadena de encabezado, consulta y ruta de acceso en función de la signatura del controlador de ruta.

Para definir el tipo de entradas que se transmiten como cuerpo de la solicitud, configure las propiedades mediante el método de extensión Accepts para definir el tipo de objeto y el tipo de contenido que espera el controlador de solicitudes. En el ejemplo siguiente, el punto de conexión acepta un objeto Todo en el cuerpo de la solicitud con un elemento content-type esperado de application/xml.

app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
  .Accepts<Todo>("application/xml");

Además del método de extensión Accepts, es posible que un tipo de parámetro describa su propia anotación mediante la implementación de la interfaz IEndpointParameterMetadataProvider. Por ejemplo, el tipo Todo siguiente agrega una anotación que requiere un cuerpo de la solicitud con un elemento content-type 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"));
    }
}

Cuando no se proporciona ninguna anotación explícita, el marco intenta determinar el tipo de solicitud predeterminado si hay un parámetro de cuerpo de la solicitud en el controlador de punto de conexión. La inferencia usa la heurística siguiente para generar la anotación:

  • Los parámetros del cuerpo de la solicitud que se leen desde un formulario mediante el atributo [FromForm] se describen con el elemento content-type multipart/form-data.
  • Todos los demás parámetros del cuerpo de la solicitud se describen con el elemento content-type application/json.
  • El cuerpo de la solicitud se trata como opcional si admite un valor NULL o si la propiedad AllowEmpty se establece en el atributo FromBody.

Compatibilidad con el control de versiones de API

Las API mínimas admiten el control de versiones de API mediante el paquete Asp.Versioning.Http. Puede encontrar ejemplos de configuración del control de versiones con API mínimas en el repositorio de control de versiones de API.

Código fuente de OpenAPI de ASP.NET Core en GitHub

Recursos adicionales

Una aplicación de API mínima puede describir la especificación de OpenAPI para controladores de rutas mediante Swashbuckle.

Para obtener información sobre la compatibilidad con OpenAPI en API basadas en controladores, consulta la versión .NET 9 de este artículo.

El código siguiente es una aplicación ASP.NET Core típica compatible con 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();

Exclusión de la descripción de OpenAPI

En el ejemplo siguiente, el punto de conexión /skipme se excluye de la generación de una descripción de 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();

Describir los tipos de respuesta

En el ejemplo siguiente se usan los tipos de resultados integrados para personalizar la respuesta:

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);

Adición de identificadores de operación a OpenAPI

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

Adición de etiquetas a la descripción de OpenAPI

En el código siguiente se usa una etiqueta de agrupación de OpenAPI:

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");