发布于 2026-01-06 0 阅读
0

每个 ASP.NET Core Web API 项目都需要什么 - 第 2 部分 - API 版本控制和 Swagger

每个 ASP.NET Core Web API 项目都需要什么 - 第 2 部分 - API 版本控制和 Swagger

上一篇文章中,我介绍了如何将 Serilog 添加到项目中并通过配置appsettings.json文件进行配置。在本文中,我将添加Swagger用于 API 文档和 API 版本控制。所有需要实现的代码都将添加到我在上一篇文章中创建的项目中。

让我们开始吧。您可能知道, API 版本控制有多种方法,例如通过 URL、HTTP 标头等等。我们将介绍通过 URL 进行 API 版本控制的方法。

步骤 1 - 安装软件包

打开cool-webpi项目并安装Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

步骤 2 - 添加版本控制配置

打开Startup.cs文件,并将以下配置添加到ConfigureServices方法中:

services.AddApiVersioning(options =>
{
    // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
    options.ReportApiVersions = true;
});

services.AddVersionedApiExplorer(options =>
{
    // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
    // note: the specified format code will format the version as "'v'major[.minor][-status]"
    options.GroupNameFormat = "'v'VVV";

    // note: this option is only necessary when versioning by url segment. the SubstitutionFormat
    // can also be used to control the format of the API version in route templates
    options.SubstituteApiVersionInUrl = true;
});
Enter fullscreen mode Exit fullscreen mode

现在运行应用程序,你会发现api-version每个 API 文档中都添加了输入框。现在调用WeatherForcastAPI 时不提供任何输入值api-version 你会收到一个错误,提示需要 API 版本信息。输入框中输入值并再次调用 API,即可获得结果。
Swagger 1
1api-version

步骤 3 - 为 API 添加版本控制

在项目根目录下创建一个新文件夹,并将其命名为 `<project_root>` 。向该文件夹Apis下添加两个子文件夹 ` <project_root>`和 ` <project_root> `。将`<project_root>` 移动到`<project_root>`。 打开文件并添加属性,然后修改属性值:ApisV1V2ControllersApis\V1
项目结构 1
WeatherForecastControllerApiVersionRoute

[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
...
Enter fullscreen mode Exit fullscreen mode

再次运行应用程序,你会发现api-version输入框已不存在: 现在在 V2 文件夹中复制一份:
Swagger 2
WeatherForecastController
替代文字

[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecast2Controller : ControllerBase
{
...
Enter fullscreen mode Exit fullscreen mode

现在我们有了两个版本的 WeatherForecast API,但是我们找不到 V2 版本的 Swagger 文档:
替代文字

请查看官方API 版本控制Github 仓库以了解更多信息。

步骤 4 - 向 Swagger 添加版本控制

创建 ASP.NET Core Web API 项目时,除非取消选中此复选框,否则默认情况下会安装 Swagger: 我们将更改 Swagger 的默认配置。
替代文字

  • 更新Swashbuckle.AspNetCore至最新版本(6 及以上)。
  • 在项目根目录创建一个新文件夹并命名,然后在该文件夹内Infrastructure添加另一个文件夹SwaggerInfrastructure
  • SwaggerDefaultValues.cs在文件夹中添加一个新文件Swagger,并复制以下代码:
/// <summary>
/// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
/// </summary>
/// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>.
/// Once they are fixed and published, this class can be removed.</remarks>
public class SwaggerDefaultValues : IOperationFilter
{
    /// <summary>
    /// Applies the filter to the specified operation using the given context.
    /// </summary>
    /// <param name="operation">The operation to apply the filter to.</param>
    /// <param name="context">The current operation filter context.</param>
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var apiDescription = context.ApiDescription;

        operation.Deprecated |= apiDescription.IsDeprecated();

        // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077
        foreach (var responseType in context.ApiDescription.SupportedResponseTypes)
        {
            // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387
            var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString();
            var response = operation.Responses[responseKey];

            foreach (var contentType in response.Content.Keys)
                if (responseType.ApiResponseFormats.All(x => x.MediaType != contentType))
                    response.Content.Remove(contentType);
        }

        if (operation.Parameters == null)
            return;

        // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
        // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
        foreach (var parameter in operation.Parameters)
        {
            var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);

            parameter.Description ??= description.ModelMetadata.Description;

            if (parameter.Schema.Default == null && description.DefaultValue != null)
            {
                // REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330
                var json = JsonSerializer.Serialize(description.DefaultValue, description.ModelMetadata.ModelType);
                parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json);
            }

            parameter.Required |= description.IsRequired;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • ConfigureSwaggerOptions.cs在文件夹中添加另一个文件Swagger,并复制以下代码:
/// <summary>
/// Configures the Swagger generation options.
/// </summary>
/// <remarks>This allows API versioning to define a Swagger document per API version after the
/// <see cref="IApiVersionDescriptionProvider"/> service has been resolved from the service container.</remarks>
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider _provider;

    /// <summary>
    /// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
    /// </summary>
    /// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
    public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => _provider = provider;

    /// <inheritdoc />
    public void Configure(SwaggerGenOptions options)
    {
        // add a swagger document for each discovered API version
        // note: you might choose to skip or document deprecated API versions differently
        foreach (var description in _provider.ApiVersionDescriptions)
            options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
    }

    private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
    {
        var info = new OpenApiInfo()
        {
            Title = "Cool Web API",
            Version = description.ApiVersion.ToString(),
            Description = "A Cool Web API Sample.",
            Contact = new OpenApiContact { Name = "Mosi Esmailpour", Email = "mo.esmp@gmail.com" },
            License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
        };

        if (description.IsDeprecated)
            info.Description += " This API version has been deprecated.";

        return info;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 打开Startup.cs文件,在ConfigureServices方法部分删除默认的 Swagger 配置:
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "CoolWebApi", Version = "v1" });
});
Enter fullscreen mode Exit fullscreen mode
  • 添加以下配置:
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddSwaggerGen(options =>
{
    // add a custom operation filter which sets default values
    options.OperationFilter<SwaggerDefaultValues>();
});
Enter fullscreen mode Exit fullscreen mode
  • Configure在方法中添加参数IApiVersionDescriptionProvider
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
Enter fullscreen mode Exit fullscreen mode
  • Configure删除 Swagger UI 默认配置的方法如下:
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "CoolWebApi v1"));
Enter fullscreen mode Exit fullscreen mode
  • 添加以下代码以配置 Swagger UI:
app.UseSwagger(options => { options.RouteTemplate = "api-docs/{documentName}/docs.json"; });
app.UseSwaggerUI(options =>
{
    options.RoutePrefix = "api-docs";
    foreach (var description in provider.ApiVersionDescriptions)
        options.SwaggerEndpoint($"/api-docs/{description.GroupName}/docs.json", description.GroupName.ToUpperInvariant());
});
Enter fullscreen mode Exit fullscreen mode

我已将默认 Swagger 路由前缀从swagger`<your_swagger_name> ` 更改为 `<your_swagger_name> api-docs`。右键单击项目,选择“路由设置”,Properties然后在“路由设置”选项卡中将Debug更改为`<your_swagger_name>`(如果您不想更改默认 Swagger 路由,请跳过此步骤)。 现在运行应用程序,您应该可以看到两个 API 文档:Launch browserapi-docs
替代文字

替代文字

步骤 5 - 向 API 文档添加 XML 注释

有时向 API 添加额外信息会很有帮助。要添加 XML 注释:

  • 在解决方案资源管理器中右键单击项目并选择“编辑” CoolWebApi.csproj,或者双击该项目。
  • 添加以下几行:
<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode
  • 打开Startup.cs文件,在ConfigureServices方法中添加以下代码services.AddSwaggerGen
services.AddSwaggerGen(options =>
{
    // add a custom operation filter which sets default values
    options.OperationFilter<SwaggerDefaultValues>();

    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    options.IncludeXmlComments(xmlPath);
});
Enter fullscreen mode Exit fullscreen mode
  • 打开WeatherForecastController类,并在方法中添加以下 XML 注释GET
/// <summary>
/// This API returns list weather forecast.
/// </summary>
[HttpGet]
public IEnumerable<WeatherForecast> Get()
Enter fullscreen mode Exit fullscreen mode

运行应用程序后,您可以在 API URL 前看到上述注释: 此外,我们还可以使用元素。元素内容可以包含文本、JSON 或 XML:
替代文字
<remarks><remarks>

/// <summary>
/// This API returns list weather forecast.
/// </summary>
/// <remarks>
/// Possible values could be:
///
///     "Freezing", "Bracing", "Chilly", "Cool", "Mild",
///     "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
///
/// Just for demonstration
///
///     GET api/v1/WeatherForecast
///     {
///     }
///     curl -X GET "https://server-url/api/v1/WeatherForecast" -H  "accept: text/plain"
///
/// </remarks>
[HttpGet]
public IEnumerable<WeatherForecast> Get()
Enter fullscreen mode Exit fullscreen mode

替代文字
响应类型和错误代码在 XML 注释和数据注解中注明:

/// <response code="200">Returns list of weather forecast</response>
/// <response code="400">Noway, just for demonstration</response>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IEnumerable<WeatherForecast> Get()
Enter fullscreen mode Exit fullscreen mode

替代文字
有关更多信息[ProducesResponseType],请参阅API 约定

步骤 6 - 从 Swagger 中隐藏属性

有时您可能想要隐藏模型中的某些属性,并且不希望它们在 Swagger 中可见。您只需使用[System.Text.Json.Serialization.JsonIgnore]特性修饰该属性即可。

public class DummyModel
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    [JsonIgnore]
    public string FullName { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

步骤 7 - 启用 JWT 承载授权

要在 Swagger 中启用Authrozie按钮,请添加以下代码:

services.AddSwaggerGen(options =>
{
    ...
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey
    });
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                },
                Scheme = "oauth2",
                Name = "Bearer",
                In = ParameterLocation.Header,
            },
            new List<string>()
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

步骤 8 - 将 API URL 转换为小写

要生成小写的 API URL,请将以下内容添加到ConfigureServices方法中:

services.Configure<RouteOptions>(options => { options.LowercaseUrls = true; });
Enter fullscreen mode Exit fullscreen mode

您可以在Github上找到本教程的源代码

文章来源:https://dev.to/moesmp/what-every-asp-net-core-web-api-project-needs-part-2-api-versioning-and-swagger-3nfm