最小 API 应用中的 OpenAPI 支持

OpenAPI 规范是用于记录 HTTP API 的编程语言不可知的标准。 此标准通过通过内置 API 和开放源代码库的组合在最小 API 中获得支持。 OpenAPI 在应用程序中的集成涉及以下三个关键方面:

  • 生成有关应用中终结点的信息。
  • 将信息收集为与 OpenAPI 架构匹配的格式。
  • 通过视觉 UI 或序列化文件公开生成的 OpenAPI 架构。

最小 API 提供内置支持,用于通过 Microsoft.AspNetCore.OpenApi 包生成有关应用中终结点的信息。 通过视觉 UI 公开生成的 OpenAPI 定义需要第三方包。

以下代码由 ASP.NET Core 最小 Web API 模板生成,并使用 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);
}

在上述突出显示的代码中:

  • Microsoft.AspNetCore.OpenApi 将在下一部分中进行介绍。
  • AddEndpointsApiExplorer:将应用配置为使用 API Explorer 发现和描述具有默认注释的终结点。 WithOpenApi 将 API Explorer 生成的匹配默认注释替代为 Microsoft.AspNetCore.OpenApi 包中生成的注释。
  • UseSwagger 添加 Swagger 中间件。 需要 Swashbuckle.AspNetCore nuget 包。
  • UseSwaggerUI 在开发模式下启用 Swagger UI 工具的嵌入版本。
  • WithName:终结点上的 IEndpointNameMetadata 用于链接生成,并被视为给定终结点的 OpenAPI 规范中的操作 ID。
  • 本文后面将介绍 WithOpenApi

Microsoft.AspNetCore.OpenApi NuGet 包

ASP.NET Core 提供 Microsoft.AspNetCore.OpenApi 包以与终结点的 OpenAPI 规范进行交互。 该包充当 Microsoft.AspNetCore.OpenApi 包中定义的 OpenAPI 模型和 Minimal API 中定义的终结点之间的链接。 该包提供一个 API,用于检查终结点的参数、响应和元数据,以构造用于描述终结点的 OpenAPI 注释类型。

Microsoft.AspNetCore.OpenApi 作为 PackageReference 添加到项目文件:

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

Swashbuckle.AspNetCoreMicrosoft.AspNetCore.OpenApi 一起使用时,必须使用 Swashbuckle.AspNetCore 6.4.0 或更高版本。 Microsoft.OpenApi 1.4.3 或更高版本必须用于在 WithOpenApi 调用中利用复制构造函数。

通过 WithOpenApi 向终结点添加 OpenAPI 注释

对终结点调用 WithOpenApi 会添加到终结点元数据。 此元数据可以:

  • Swashbuckle.AspNetCore 等第三方包中使用。
  • 显示在 Swagger 用户界面或为定义 API 而生成的 YAML 或 JSON 中。
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();

修改 WithOpenApi 中的 OpenAPI 注释

WithOpenApi 方法接受可用于修改 OpenAPI 注释的函数。 例如,在以下代码中,将说明添加到终结点的第一个参数:

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

将操作 ID 添加到 OpenAPI

操作 ID 用于唯一标识 OpenAPI 中的给定终结点。 WithName 扩展方法可用于设置供方法使用的操作 ID。

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

也可以直接在 OpenAPI 注释上设置 OperationId 属性。

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

将标记添加到 OpenAPI 说明

OpenAPI 支持使用标记对象对操作进行分类。 这些标记通常用于对 Swagger UI 中的操作进行分组。 可以通过调用具有所需标记的终结点上的 WithTags 扩展方法,将这些标记添加到操作中。

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

或者,可以通过 WithOpenApi 扩展方法在 OpenAPI 注释上设置 OpenApiTags 列表。

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

添加终结点摘要或说明

可以通过调用 WithOpenApi 扩展方法添加终结点摘要和说明。 在以下代码中,直接在 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"
    });

排除 OpenAPI 说明

在下面的示例中,/skipme 终结点从生成 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();

将 API 标记为已过时

若要将终结点标记为已过时,请在 OpenAPI 注释上设置 Deprecated 属性。

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

描述响应类型

OpenAPI 支持提供从 API 返回的响应的说明。 最小 API 支持使用三种策略来设置终结点的响应类型:

Produces 扩展方法可用于将 Produces 元数据添加到终结点。 如果未提供任何参数,则扩展方法将在 200 状态代码和 application/json 内容类型下为目标类型填充元数据。

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

在终结点路由处理程序中使用其实现中的 TypedResults,就可以自动包含终结点的响应类型元数据。 例如,以下代码通过在 200 状态代码和 application/json 内容类型下的响应,自动对终结点进行注释。

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

ProblemDetails 设置响应

为可能返回 ProblemDetails 响应的终结点设置响应类型时,ProducesProblem 扩展方法或 TypedResults.Problem 可用于向终结点的元数据添加相应的注释。

如果上述策略之一未提供显式注释,则框架会尝试通过检查响应的签名来确定默认响应类型。 此默认响应是在 OpenAPI 定义中的 200 状态代码下填充的。

多个响应类型

如果终结点可以在不同的方案中返回不同的响应类型,则可以通过以下方式提供元数据:

  • 多次调用 Produces 扩展方法,如以下示例所示:

    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);
    
  • 在签名中使用 Results<TResult1,TResult2,TResultN>,在处理程序的正文中使用 TypedResults,如以下示例所示:

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

    Results<TResult1,TResult2,TResultN>联合类型声明路由处理程序返回多个 IResult 实现具体类型,并且实现 IEndpointMetadataProvider 的其中任何一个类型都将参与终结点的元数据。

    联合类型实现隐式强制转换运算符。 通过这些运算符,编译器可以自动将泛型参数中指定的类型转换为联合类型的实例。 此功能增加了一个好处,即提供编译时检查,路由处理程序只返回声明它的结果。 尝试返回未声明为 Results<TResult1,TResult2,TResultN> 泛型参数之一的类型会导致编译错误。

描述请求正文和参数

除了描述终结点返回的类型外,OpenAPI 还支持对 API 使用的输入进行注释。 这些输入分为两个类别:

  • 出现在路径、查询字符串、标头或 cookie 中的参数
  • 作为请求正文的一部分传输的数据

框架根据路由处理程序的签名自动推断路径、查询和标头字符串中请求参数的类型。

若要定义作为请求正文传输的输入类型,请使用 Accepts 扩展方法配置属性,以定义请求处理程序预期的对象类型和内容类型。 在以下示例中,终结点接受请求正文中的 Todo 对象,其预期内容类型为 application/xml

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

除了 Accepts 扩展方法外,参数类型还可以通过实现 IEndpointParameterMetadataProvider 接口来描述自己的注释。 例如,以下 Todo 类型添加一个注释,该注释需要具有 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"));
    }
}

如果未提供显式注释,则框架将尝试确定默认请求类型(如果终结点处理程序中有请求正文参数)。 推理使用以下启发法生成注释:

  • 通过 [FromForm] 属性从窗体读取的请求正文参数使用 multipart/form-data 内容类型进行描述。
  • 所有其他请求正文参数均使用 application/json 内容类型进行描述。
  • 如果请求正文可为空,或者在 FromBody 特性上设置 AllowEmpty 属性,则请求正文被视为可选。

支持 API 版本控制

最小 API 支持通过 Asp.Versioning.Http 包进行 API 版本控制。 使用最小 API 配置版本控制的示例请见 API 版本控制存储库

GitHub 上的 ASP.NET Core OpenAPI 源代码

其他资源

OpenAPI 规范是用于记录 HTTP API 的编程语言不可知的标准。 此标准通过通过内置 API 和开放源代码库的组合在最小 API 中获得支持。 OpenAPI 在应用程序中的集成涉及以下三个关键方面:

  • 生成有关应用中终结点的信息。
  • 将信息收集为与 OpenAPI 架构匹配的格式。
  • 通过视觉 UI 或序列化文件公开生成的 OpenAPI 架构。

最小 API 提供内置支持,用于通过 Microsoft.AspNetCore.OpenApi 包生成有关应用中终结点的信息。 通过视觉 UI 公开生成的 OpenAPI 定义需要第三方包。

以下代码由 ASP.NET Core 最小 Web API 模板生成,并使用 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);
}

在上述突出显示的代码中:

  • Microsoft.AspNetCore.OpenApi 将在下一部分中进行介绍。
  • AddEndpointsApiExplorer:将应用配置为使用 API Explorer 发现和描述具有默认注释的终结点。 WithOpenApi 将 API Explorer 生成的匹配默认注释替代为 Microsoft.AspNetCore.OpenApi 包中生成的注释。
  • UseSwagger 添加 Swagger 中间件
  • `UseSwaggerUI` 启用 Swagger UI 工具的嵌入版本。
  • WithName:终结点上的 IEndpointNameMetadata 用于链接生成,并被视为给定终结点的 OpenAPI 规范中的操作 ID。
  • 本文后面将介绍 WithOpenApi

Microsoft.AspNetCore.OpenApi NuGet 包

ASP.NET Core 提供 Microsoft.AspNetCore.OpenApi 包以与终结点的 OpenAPI 规范进行交互。 该包充当 Microsoft.AspNetCore.OpenApi 包中定义的 OpenAPI 模型和 Minimal API 中定义的终结点之间的链接。 该包提供一个 API,用于检查终结点的参数、响应和元数据,以构造用于描述终结点的 OpenAPI 注释类型。

Microsoft.AspNetCore.OpenApi 作为 PackageReference 添加到项目文件:

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

Swashbuckle.AspNetCoreMicrosoft.AspNetCore.OpenApi 一起使用时,必须使用 Swashbuckle.AspNetCore 6.4.0 或更高版本。 Microsoft.OpenApi 1.4.3 或更高版本必须用于在 WithOpenApi 调用中利用复制构造函数。

通过 WithOpenApi 向终结点添加 OpenAPI 注释

对终结点调用 WithOpenApi 会添加到终结点元数据。 此元数据可以:

  • Swashbuckle.AspNetCore 等第三方包中使用。
  • 显示在 Swagger 用户界面或为定义 API 而生成的 YAML 或 JSON 中。
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();

修改 WithOpenApi 中的 OpenAPI 注释

WithOpenApi 方法接受可用于修改 OpenAPI 注释的函数。 例如,在以下代码中,将说明添加到终结点的第一个参数:

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

将操作 ID 添加到 OpenAPI

操作 ID 用于唯一标识 OpenAPI 中的给定终结点。 WithName 扩展方法可用于设置供方法使用的操作 ID。

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

也可以直接在 OpenAPI 注释上设置 OperationId 属性。

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

将标记添加到 OpenAPI 说明

OpenAPI 支持使用标记对象对操作进行分类。 这些标记通常用于对 Swagger UI 中的操作进行分组。 可以通过调用具有所需标记的终结点上的 WithTags 扩展方法,将这些标记添加到操作中。

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

或者,可以通过 WithOpenApi 扩展方法在 OpenAPI 注释上设置 OpenApiTags 列表。

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

添加终结点摘要或说明

可以通过调用 WithOpenApi 扩展方法添加终结点摘要和说明。 在以下代码中,直接在 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"
    });

排除 OpenAPI 说明

在下面的示例中,/skipme 终结点从生成 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();

将 API 标记为已过时

若要将终结点标记为已过时,请在 OpenAPI 注释上设置 Deprecated 属性。

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

描述响应类型

OpenAPI 支持提供从 API 返回的响应的说明。 最小 API 支持使用三种策略来设置终结点的响应类型:

Produces 扩展方法可用于将 Produces 元数据添加到终结点。 如果未提供任何参数,则扩展方法将在 200 状态代码和 application/json 内容类型下为目标类型填充元数据。

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

在终结点路由处理程序中使用其实现中的 TypedResults,就可以自动包含终结点的响应类型元数据。 例如,以下代码通过在 200 状态代码和 application/json 内容类型下的响应,自动对终结点进行注释。

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

ProblemDetails 设置响应

为可能返回 ProblemDetails 响应的终结点设置响应类型时,ProducesProblem 扩展方法或 TypedResults.Problem 可用于向终结点的元数据添加相应的注释。

如果上述策略之一未提供显式注释,则框架会尝试通过检查响应的签名来确定默认响应类型。 此默认响应是在 OpenAPI 定义中的 200 状态代码下填充的。

多个响应类型

如果终结点可以在不同的方案中返回不同的响应类型,则可以通过以下方式提供元数据:

  • 多次调用 Produces 扩展方法,如以下示例所示:

    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);
    
  • 在签名中使用 Results<TResult1,TResult2,TResultN>,在处理程序的正文中使用 TypedResults,如以下示例所示:

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

    Results<TResult1,TResult2,TResultN>联合类型声明路由处理程序返回多个 IResult 实现具体类型,并且实现 IEndpointMetadataProvider 的其中任何一个类型都将参与终结点的元数据。

    联合类型实现隐式强制转换运算符。 通过这些运算符,编译器可以自动将泛型参数中指定的类型转换为联合类型的实例。 此功能增加了一个好处,即提供编译时检查,路由处理程序只返回声明它的结果。 尝试返回未声明为 Results<TResult1,TResult2,TResultN> 泛型参数之一的类型会导致编译错误。

描述请求正文和参数

除了描述终结点返回的类型外,OpenAPI 还支持对 API 使用的输入进行注释。 这些输入分为两个类别:

  • 出现在路径、查询字符串、标头或 cookie 中的参数
  • 作为请求正文的一部分传输的数据

框架根据路由处理程序的签名自动推断路径、查询和标头字符串中请求参数的类型。

若要定义作为请求正文传输的输入类型,请使用 Accepts 扩展方法配置属性,以定义请求处理程序预期的对象类型和内容类型。 在以下示例中,终结点接受请求正文中的 Todo 对象,其预期内容类型为 application/xml

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

除了 Accepts 扩展方法外,参数类型还可以通过实现 IEndpointParameterMetadataProvider 接口来描述自己的注释。 例如,以下 Todo 类型添加一个注释,该注释需要具有 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"));
    }
}

如果未提供显式注释,则框架将尝试确定默认请求类型(如果终结点处理程序中有请求正文参数)。 推理使用以下启发法生成注释:

  • 通过 [FromForm] 属性从窗体读取的请求正文参数使用 multipart/form-data 内容类型进行描述。
  • 所有其他请求正文参数均使用 application/json 内容类型进行描述。
  • 如果请求正文可为空,或者在 FromBody 特性上设置 AllowEmpty 属性,则请求正文被视为可选。

支持 API 版本控制

最小 API 支持通过 Asp.Versioning.Http 包进行 API 版本控制。 使用最小 API 配置版本控制的示例请见 API 版本控制存储库

GitHub 上的 ASP.NET Core OpenAPI 源代码

其他资源

应用可以使用 Swashbuckle 描述路由处理程序的 OpenAPI 规范

以下代码是具有 OpenAPI 支持的典型 ASP.NET Core 应用:

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

排除 OpenAPI 说明

在下面的示例中,/skipme 终结点从生成 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();

描述响应类型

以下示例使用内置结果类型自定义响应:

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

将操作 ID 添加到 OpenAPI

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

将标记添加到 OpenAPI 说明

以下代码使用 OpenAPI 分组标记

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