通过 Swashbuckle 创建 "latest" 版本的 swagger 文档

Creating a "latest" version of swagger documentation via Swashbuckle

我正在研究面向 API 的 public,使用 Swashbuckle.AspNetCoreReDoc 用于文档,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);
                }
            }
        }
    }
}