每个 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;
});
现在运行应用程序,你会发现api-version每个 API 文档中都添加了输入框。现在调用WeatherForcastAPI 时不提供任何输入值api-version: 你会收到一个错误,提示需要 API 版本信息。在输入框中输入值并再次调用 API,即可获得结果。
1api-version
步骤 3 - 为 API 添加版本控制
在项目根目录下创建一个新文件夹,并将其命名为 `<project_root>` 。向该文件夹Apis下添加两个子文件夹 ` <project_root>`和 ` <project_root> `。将`<project_root>` 移动到`<project_root>`。 打开文件并添加属性,然后修改属性值:ApisV1V2ControllersApis\V1
WeatherForecastControllerApiVersionRoute
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
...
再次运行应用程序,你会发现api-version输入框已不存在: 现在在 V2 文件夹中复制一份:
WeatherForecastController
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecast2Controller : ControllerBase
{
...
现在我们有了两个版本的 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;
}
}
}
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;
}
}
- 打开
Startup.cs文件,在ConfigureServices方法部分删除默认的 Swagger 配置:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "CoolWebApi", Version = "v1" });
});
- 添加以下配置:
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddSwaggerGen(options =>
{
// add a custom operation filter which sets default values
options.OperationFilter<SwaggerDefaultValues>();
});
Configure在方法中添加参数IApiVersionDescriptionProvider:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
Configure删除 Swagger UI 默认配置的方法如下:
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "CoolWebApi v1"));
- 添加以下代码以配置 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());
});
我已将默认 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>
- 打开
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);
});
- 打开
WeatherForecastController类,并在方法中添加以下 XML 注释GET:
/// <summary>
/// This API returns list weather forecast.
/// </summary>
[HttpGet]
public IEnumerable<WeatherForecast> Get()
运行应用程序后,您可以在 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()
/// <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()

有关更多信息[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; }
}
步骤 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>()
}
});
});
步骤 8 - 将 API URL 转换为小写
要生成小写的 API URL,请将以下内容添加到ConfigureServices方法中:
services.Configure<RouteOptions>(options => { options.LowercaseUrls = true; });
您可以在Github上找到本教程的源代码。
文章来源:https://dev.to/moesmp/what-every-asp-net-core-web-api-project-needs-part-2-api-versioning-and-swagger-3nfm
