通过 Swashbuckle 创建 "latest" 版本的 swagger 文档
Creating a "latest" version of swagger documentation via Swashbuckle
我正在研究面向 API 的 public,使用 Swashbuckle.AspNetCore
和 ReDoc
用于文档,Microsoft.AspNetCore.Mvc.Versioning
使用属性对我们的控制器进行版本控制。
我们希望有一个 swagger 文档来显示我们端点的所有最新版本,以便首次访问 API 的人们更容易选择正确的版本。
我目前的尝试是创建一个 'v0' 并将该版本应用于所有最新版本的控制器。然后我使用操作过滤器将 'v0' 替换为最新版本:
public class LatestVersionOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var version = (context.MethodInfo.GetCustomAttributes<ApiVersionAttribute>()
.Union(context.MethodInfo.DeclaringType.GetCustomAttributes<ApiVersionAttribute>())
.SelectMany(x => x.Versions)
.OrderByDescending(x => x.MajorVersion))
.FirstOrDefault(x => x.MajorVersion != 0);
if (version != null && context.ApiDescription.RelativePath.Contains("v0"))
{
context.ApiDescription.RelativePath = context.ApiDescription.RelativePath
.Replace("v0", $"v{version.MajorVersion}");
}
}
}
这在大多数情况下都有效,但有时它似乎没有启动,你最终会得到一堆带有 'v0' 的 URL。过滤器运行,但似乎没有反映在生成的 swagger 文档中。
有没有更好的方法来实现我们的目标?我试图用 DocInclusionPredicate
写一些东西,但我似乎无法得到我想要的东西。
经过进一步挖掘,我发现了这个老问题:
https://github.com/domaindrivendev/Swashbuckle/issues/361
我以前尝试过文档过滤器,但感觉走错了路。看到这个问题,我又做了一次,下面的内容现在对我们有用:
public class LatestVersionDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var path in swaggerDoc.Paths.ToList())
{
if (!path.Key.Contains("v0"))
{
continue;
}
var apiDescription = context.ApiDescriptions.FirstOrDefault(x => x.RelativePath == path.Key.TrimStart('/'));
if (apiDescription != null && apiDescription.TryGetMethodInfo(out var methodInfo))
{
var latestVersion = methodInfo.GetCustomAttributes<ApiVersionAttribute>()
.Union(methodInfo.DeclaringType.GetCustomAttributes<ApiVersionAttribute>())
.SelectMany(x => x.Versions)
.OrderByDescending(x => x.MajorVersion)
.FirstOrDefault(x => x.MajorVersion != 0);
if (latestVersion != null)
{
swaggerDoc.Paths.Remove(path.Key);
var latestUrl = path.Key.Replace("/v0/", $"/v{latestVersion.MajorVersion}/");
swaggerDoc.Paths.Add(latestUrl, path.Value);
}
}
}
}
}
我正在研究面向 API 的 public,使用 Swashbuckle.AspNetCore
和 ReDoc
用于文档,Microsoft.AspNetCore.Mvc.Versioning
使用属性对我们的控制器进行版本控制。
我们希望有一个 swagger 文档来显示我们端点的所有最新版本,以便首次访问 API 的人们更容易选择正确的版本。
我目前的尝试是创建一个 'v0' 并将该版本应用于所有最新版本的控制器。然后我使用操作过滤器将 'v0' 替换为最新版本:
public class LatestVersionOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var version = (context.MethodInfo.GetCustomAttributes<ApiVersionAttribute>()
.Union(context.MethodInfo.DeclaringType.GetCustomAttributes<ApiVersionAttribute>())
.SelectMany(x => x.Versions)
.OrderByDescending(x => x.MajorVersion))
.FirstOrDefault(x => x.MajorVersion != 0);
if (version != null && context.ApiDescription.RelativePath.Contains("v0"))
{
context.ApiDescription.RelativePath = context.ApiDescription.RelativePath
.Replace("v0", $"v{version.MajorVersion}");
}
}
}
这在大多数情况下都有效,但有时它似乎没有启动,你最终会得到一堆带有 'v0' 的 URL。过滤器运行,但似乎没有反映在生成的 swagger 文档中。
有没有更好的方法来实现我们的目标?我试图用 DocInclusionPredicate
写一些东西,但我似乎无法得到我想要的东西。
经过进一步挖掘,我发现了这个老问题: https://github.com/domaindrivendev/Swashbuckle/issues/361
我以前尝试过文档过滤器,但感觉走错了路。看到这个问题,我又做了一次,下面的内容现在对我们有用:
public class LatestVersionDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var path in swaggerDoc.Paths.ToList())
{
if (!path.Key.Contains("v0"))
{
continue;
}
var apiDescription = context.ApiDescriptions.FirstOrDefault(x => x.RelativePath == path.Key.TrimStart('/'));
if (apiDescription != null && apiDescription.TryGetMethodInfo(out var methodInfo))
{
var latestVersion = methodInfo.GetCustomAttributes<ApiVersionAttribute>()
.Union(methodInfo.DeclaringType.GetCustomAttributes<ApiVersionAttribute>())
.SelectMany(x => x.Versions)
.OrderByDescending(x => x.MajorVersion)
.FirstOrDefault(x => x.MajorVersion != 0);
if (latestVersion != null)
{
swaggerDoc.Paths.Remove(path.Key);
var latestUrl = path.Key.Replace("/v0/", $"/v{latestVersion.MajorVersion}/");
swaggerDoc.Paths.Add(latestUrl, path.Value);
}
}
}
}
}