.NET webApi 中Swagger默认是不会进行任何分组的,就只是罗列出来罢了。
这样当用的时间长了,api变多了,项目变得复杂了,看起来就会很难受了,这时候对swagger的分组就变得很有必要了。
网上的浏览下来,主要有两种分组方式:自定义分组、通过版本控制分组
但这两种情况实际上在开发中都会出现,所以就在想要怎么才能同时满足两种分组方式,但实际上这两种方式都需要用到GroupName参数,其中一个总会覆盖另一个,网上也没有搜到现成的,只有自己动手撸了 😅 。
(纯粹自己瞎整出来的,不一定是最佳方案,可以当作一种方向参考,也欢迎评论区讨论)
先看最终显示效果吧:
这里可以看到每个文档的分组条件格式是【自定义】:版本号
,是不是看着还行 😋
虽然下拉框看着挺奇怪的,但在不改动前端的情况下也就只能先这么整了 😅 。
下面直接看代码吧~
首先是Swagger的配置信息
这里定义了一个自定义分组Config
,其中第一个是 默认分组,给那些不设置自定义分组的api留的,这个是必须要有的,后面的自己根据实际设置就行
//Swagger 文档配置
"Swagger": {
"Doc": {
"Content": {
"Name": "workbench-for-mr.liu",
"Email": "---"
},
"Description": ".NET6 WebAPI 文档 By workbench",
"Title": ".NET6 WebAPI 文档"
},
//自定义分组
"Config": [
{
"GroupName": "",
"Title": "【默认分组】"
},
{
"GroupName": "Open",
"Title": "【开放接口】"
},
{
"GroupName": "Test",
"Title": "【内部测试】"
}
]
}
Program.cs
配置Swgger
根据前一步配置的自定义分组和api的版本控制(并过滤掉不需要的分组)生成Swagger文档
//Swagger
builder.Services.AddSwaggerGen(c =>
{
#region 文档分组(自定义+版本控制)
//根据API版本信息生成的API文档
var provider = builder.Services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
var _config = AppConfig.Get_appsettting<List<SwaggerConfig.groupConfig>>("Swagger:Config");
//当前所有的api
var _apiDescriptionsProvider = builder.Services.BuildServiceProvider().GetService<Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionGroupCollectionProvider>();
var appApis = _apiDescriptionsProvider.ApiDescriptionGroups.Items
.SelectMany(group => group.Items);
//自定义分组
_config.ForEach(group =>
{
//版本号分组
foreach (var description in provider.ApiVersionDescriptions)
{
//是否存在符合的api
var _api = appApis.Where(x => ((group.GroupName.Equals("") && !x.RelativePath.Contains("{GName-")) || (!group.GroupName.Equals("") && x.RelativePath.Contains($"GName-{group.GroupName}"))) && description.GroupName.Equals(x.GroupName));
if (_api.Count() > 0)
{
string _GName = (string.IsNullOrWhiteSpace(group.GroupName) ? "" : group.GroupName + ":") + description.GroupName;
c.SwaggerDoc(_GName, new OpenApiInfo
{
Contact = new OpenApiContact
{
Name = AppConfig.Get_appsettting("Swagger:Doc:Content:Name"),
Email = AppConfig.Get_appsettting("Swagger:Doc:Content:Email")
},
Description = AppConfig.Get_appsettting("Swagger:Doc:Description"),
Title = group.Title,
Version = "v" + description.ApiVersion.ToString()
});
}
}
});
//在Swagger文档显示的API地址中将版本信息参数替换为实际的版本号
c.DocInclusionPredicate((version, apiDescription) =>
{
//= "api/{GName-Sys}/v{version}/WeatherForecast"
var _GName = _config.Where(x => !string.IsNullOrWhiteSpace(x.GroupName) && apiDescription.RelativePath.Contains(x.GroupName)).FirstOrDefault()?.GroupName;
var _GName2 = apiDescription.GroupName;
if (!string.IsNullOrWhiteSpace(_GName))
_GName2 = _GName + ":" + _GName2;
if (!version.Equals(_GName2))
return false;
IEnumerable<string>? values = apiDescription!.RelativePath.Split('/').Select(x => Regex.Replace(x.Replace("v{version}", apiDescription.GroupName), "\\{GName\\-[a-zA-Z0-9]+\\}", _GName ?? "")).Where(x => !string.IsNullOrWhiteSpace(x));
apiDescription.RelativePath = String.Join("/", values);
return true;
});
#endregion
SwaggerUI 也需要同时设置
app.UseSwaggerUI(c =>
{
#region 文档分组(自定义+版本控制)
var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
var _config = AppConfig.Get_appsettting<List<SwaggerConfig.groupConfig>>("Swagger:Config");
//当前所有的api
var _apiDescriptionsProvider = builder.Services.BuildServiceProvider().GetService<Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionGroupCollectionProvider>();
var appApis = _apiDescriptionsProvider.ApiDescriptionGroups.Items
.SelectMany(group => group.Items);
_config.ForEach(group =>
{
foreach (var description in provider.ApiVersionDescriptions.Reverse())
{
//是否存在符合的api
var _api = appApis.Where(x => ((group.GroupName.Equals("") && !x.RelativePath.Contains("{GName-")) || (!group.GroupName.Equals("") && x.RelativePath.Contains($"GName-{group.GroupName}"))) && description.GroupName.Equals(x.GroupName));
if (_api.Count() > 0)
{
c.SwaggerEndpoint($"/swagger/{(string.IsNullOrWhiteSpace(group.GroupName) ? "" : group.GroupName + ":") + description.GroupName}/swagger.json", group.Title + ":" + description.GroupName); //分组显示
}
}
});
#endregion
});
最后看看结果吧
使用方式,[ApiVersion("0")]
不变,和之前一样就行,至于[ApiExplorerSettings(GroupName ="")]
由于会和API版本控制冲突,所以这个就不能用了,改成直接在路由前面加上特殊识别内容{GName-Test}/
,其中Test
需要和前面设置的 自定义分组 配置项的GroupName
一致才行,完整情况就像下面这样:
/// <summary>
/// 示例控制器
/// </summary>
[Route("{GName-Test}/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("0")]
//[ApiExplorerSettings(GroupName ="")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public ReturnInfo get()
{
return new ReturnInfo() { message="okok!"};
}
[HttpGet("ssssss")]
[ApiVersion("3")]
public ReturnInfo hhh()
{
return new ReturnInfo() { message="okok!"};
}
}
可以看到,这里有两个api,其中一个将版本设置成了v3
,Controller上添加了自定义分组Test
,所以等会就会分成Test:v0
和Test:v3
两个文档
但是,请求路径会多带一个Test
,也会多出一个路径参数
第一个/Test/
,必须要留着(其实看着条理也挺清晰的,留着也还不错)
第二个是路径参数,是因为我前面写的{}
的关系,其实直接去掉就行了(但是我还是想跟正常的部分有个区分),也可以像下面这样,直接删除GName
路径参数就行了(由于这是之前版本控制就配置好了的,所以直接就复制一份改改就行了)
/// <summary>
/// 取消swagger文档需要输入版本信息
/// </summary>
public class RemoveVersionFromParameter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var versionParameter = operation.Parameters.FirstOrDefault(p => p.Name == "version");
if (versionParameter != null)
{
operation.Parameters.Remove(versionParameter);
}
//也删掉GName路径参数
var versionParameter2 = operation.Parameters.FirstOrDefault(p => p.Name.Contains("gName"));
if (versionParameter2 != null)
{
operation.Parameters.Remove(versionParameter2);
}
}
}
这时候再看也就没有多余的参数了,请求也是没问题的
完事,大概就是这样 😏
评论 (0)