无法使用 Swashbuckle 创建多个 OpenApi 规范

Unable to create multiple OpenApi specifications with Swashbuckle

我正在使用 Asp.Net Boilerplate / Asp.Net Zero

构建解决方案

我在 Startup.cs 中创建了两个 OpenApi 规范(HostApiv1TenantApiv1):

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("HostApiv1", new Info { Title = "Host API v1", Version = "v1" });
    options.SwaggerDoc("TenantApiv1", new Info { Title = "Tenant API v1", Version = "v1" });

    options.DocInclusionPredicate((docName, description) => true);
    options.IgnoreObsoleteActions();
    options.IgnoreObsoleteProperties();
    options.OrderActionsBy((apiDesc) => $"{apiDesc.RelativePath}");
    options.DescribeAllEnumsAsStrings();
});
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint(_appConfiguration["App:HostApiSwaggerEndPoint"], "Host API v1");
    options.SwaggerEndpoint(_appConfiguration["App:TenantApiSwaggerEndPoint"], "Tenant API v1");
    //...
});

然而,当我用 [ApiExplorerSettings(GroupName = "HostApiv1")] 装饰我的 AppService 类 时,分组被忽略并且标签(AppService 控制器)及其所有操作(动作/方法)仍然出现在两个文件下。

知道哪里出了问题,或者我该如何调试它?

Swashbuckle 依赖于 ApiExplorerApiExplorer 属性的使用限制我们只能为每个控制器/动作指定一个组名。 ABP 服务代理是通过 NSwag 为 angular 项目生成的,似乎在此过程中依赖关系被打破。

解决方法是创建自定义 Attribute 来为应用服务控制器或操作定界一个或多个组名,然后在 DocInclusionPredicateFunction 选项中使用反射来检索组名动作或其包含的控制器。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerDocAttribute: Attribute
{
    public SwaggerDocAttribute(params string[] includeInDocuments)
    {
        IncludeInDocuments = includeInDocuments;
    }

    public string[] IncludeInDocuments { get; }
}

options.DocInclusionPredicate((docName, apiDesc) =>
{
    if (!apiDesc.ActionDescriptor.IsControllerAction())
    {
        return false;
    }

    apiDesc.TryGetMethodInfo(out MethodInfo methodInfo);

    var actionDocs = methodInfo.GetCustomAttributes<SwaggerDocAttribute>()
        .SelectMany(a => a.IncludeInDocuments);

    var controllerDocs = methodInfo.DeclaringType.GetCustomAttributes<SwaggerDocAttribute>()
        .SelectMany(a => a.IncludeInDocuments);

    switch (docName)
    {
        case "HostApiv1":
            return apiDesc.GroupName == null || 
            actionDocs.Contains("HostApiv1") || 
            controllerDocs.Contains("HostApiv1");
        case "TenantApiv1":
            return apiDesc.GroupName == null ||
            actionDocs.Contains("TenantApiv1") || 
            controllerDocs.Contains("TenantApiv1");
        default:
            return true;
    }
});

用法

[DisableAuditing]
[AbpAuthorize(AppPermissions.HostSpecific.Dashboard.Access)]
//[ApiExplorerSettings(GroupName = "HostApiv1")] // <== Don't use this
[SwaggerDoc("HostApiv1")] // <== Use this in stead
public class MyDemoAppService : ZenDetectAppServiceBase, IHostDashboardAppService
{
        //...
}