无法使用 Swashbuckle 创建多个 OpenApi 规范
Unable to create multiple OpenApi specifications with Swashbuckle
我正在使用 Asp.Net Boilerplate / Asp.Net Zero
构建解决方案
我在 Startup.cs 中创建了两个 OpenApi 规范(HostApiv1
和 TenantApiv1
):
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 依赖于 ApiExplorer
,ApiExplorer
属性的使用限制我们只能为每个控制器/动作指定一个组名。 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
{
//...
}
我正在使用 Asp.Net Boilerplate / Asp.Net Zero
构建解决方案我在 Startup.cs 中创建了两个 OpenApi 规范(HostApiv1
和 TenantApiv1
):
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 依赖于 ApiExplorer
,ApiExplorer
属性的使用限制我们只能为每个控制器/动作指定一个组名。 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
{
//...
}