在 asp.net core 3.1 web api 中,分组和版本控制不能很好地协同工作

Grouping and Versioning not working well together in swagger in asp.net core 3.1 web api

我正在使用 Asp.Net Core 3.1 构建我的 API。我正在使用 swagger 为我的 API 生成文档。我决定根据控制器对我的 swagger 文档进行分组。所以我最终这样做了,

启动 - 配置服务:

options.SwaggerDoc(
    "LibraryOpenAPISpecificationCategories",
    ...

启动-配置:

options.SwaggerEndpoint(
    "/swagger/LibraryOpenAPISpecificationCategories/swagger.json",
    "Library API (Categories)");

控制器:

[Route("api/categories")]
[ApiController]
[ApiExplorerSettings(GroupName = "LibraryOpenAPISpecificationCategories")]
public class CategoriesController : ControllerBase

到目前为止一切正常。当我添加版本控制时,Swagger 文档停止显示控制器中的方法。我试图在版本中进行分组,这样每个版本都会有这样的组,

V1 -> LibraryOpenAPISpecificationCategories

V1 -> LibraryOpenAPISpecificationItems

V2 -> LibraryOpenAPISpecificationCategories

V2 -> LibraryOpenAPISpecificationItems

这是我做的,

启动 - 配置服务:

services.AddVersionedApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VV";
});

services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.ReportApiVersions = true;
});

var apiVersionDescriptionProvider =
    services.BuildServiceProvider().GetService<IApiVersionDescriptionProvider>();

services.AddSwaggerGen(options =>
{
    foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
    {
        options.SwaggerDoc(
            $"LibraryOpenAPISpecificationCategories{description.GroupName}",
            ...

启动-配置:

app.UseSwaggerUI(options =>
{
    foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
    {
        options.SwaggerEndpoint(
            $"/swagger/LibraryOpenAPISpecificationCategories{description.GroupName}/swagger.json",
            $"Library API (Categories) {description.GroupName.ToUpperInvariant()}");

控制器:

[Route("api/categories")]
[ApiController]
[ApiExplorerSettings(GroupName = "LibraryOpenAPISpecificationCategories")]
public class CategoriesController : ControllerBase

swagger文档没有显示错误。请帮我弄清楚我哪里出错了。我错过了什么吗?

经过一些分析,我发现我在 ConfigureServices 中的 AddSwaggerGen 中遗漏了 DocInclusionPredicate

我是这样解决的,

options.DocInclusionPredicate((documentName, apiDescription) =>
{
    var actionApiVersionModel = apiDescription.ActionDescriptor
    .GetApiVersionModel(ApiVersionMapping.Explicit | ApiVersionMapping.Implicit);

    var apiExplorerSettingsAttribute = (ApiExplorerSettingsAttribute)apiDescription.ActionDescriptor.EndpointMetadata.First(x => x.GetType().Equals(typeof(ApiExplorerSettingsAttribute)));

    if (actionApiVersionModel == null)
    {
        return true;
    }

    if (actionApiVersionModel.DeclaredApiVersions.Any())
    {
        return actionApiVersionModel.DeclaredApiVersions.Any(v =>
        $"{apiExplorerSettingsAttribute.GroupName}v{v.ToString()}" == documentName);
    }
    return actionApiVersionModel.ImplementedApiVersions.Any(v =>
        $"{apiExplorerSettingsAttribute.GroupName}v{v.ToString()}" == documentName);
});

希望这对那里的人有所帮助。

由于一些人在不同的地方提出了这个要求,下面是您如何实现自定义 IApiDescriptionProvider。它只是在处理结束时更新 ApiDescription.GroupName。这将完全独立于 Swashbuckle 或任何其他 OpenAPI/Swagger 文档生成器:

public class CollateApiDescriptionProvider : IApiDescriptionProvider
{
    readonly IOptions<ApiExplorerOptions> options;

    public CollateApiDescriptionProvider( IOptions<ApiExplorerOptions> options ) =>
        this.options = options;

    public int Order => 0;

    public void OnProvidersExecuting( ApiDescriptionProviderContext context ) { }

    public void OnProvidersExecuted( ApiDescriptionProviderContext context )
    {
        var results = context.Results;
        var format = options.Value.GroupNameFormat;
        var text = new StringBuilder();

        for ( var i = 0; i < results.Count; i++ )
        {
            var result = results[i];
            var action = result.ActionDescriptor;
            var version = result.GetApiVersion();
            var groupName = action.GetProperty<ApiDescriptionActionData>()?.GroupName;

            text.Clear();

            // add the formatted API version according to the configuration
            text.Append( version.ToString( format, null ) )

            // if there's a group name, prepend it
            if ( !string.IsNullOrEmpty( groupName ) )
            {
                text.Insert( 0, ' ' );
                text.Insert( 0, groupName );
            }

            result.GroupName = text.ToString();
        }
    }
}

要注册您的新提供商,请将其添加到服务集合中:

services.TryAddEnumerable(
    ServiceDescriptor.Transient<IApiDescriptionProvider, CollateApiDescriptionProvider>() );

NOTE: this should occur after services.AddApiVersioning()

您可以随心所欲地使用组名,但请注意,您不能创建多级分组。它根本不受开箱即用的支持。在大多数情况下,每个 API 版本只能有一个 OpenAPI/Swagger 文档。这是因为 URL 在文档中必须是唯一的。

技术上 可以在多个级别上进行分组,但这需要对 UI 和文档生成过程进行一些更改。我只见过少数人愿意付出这么多努力。他们有效地创建了自己的 UIs 和文档生成后端。