在 SwashBuckle 中使用 IOperationFilter 删除路由

Remove a route with IOperationFilter in SwashBuckle

我正在寻找一种方法,以可配置的方式使用 SwashBuckle 在 Swagger 文档中 show/hide WebAPI 路由。添加 [ApiExplorerSettings(IgnoreApi = true)] 确实会隐藏路由,但每次我想要更改时都需要重新编译。

我研究过创建一个 IOperationFilter 来使用我定义的自定义属性。这样我就可以用 [SwaggerTag("MobileOnly")] 装饰路线并检查 web.config 或其他东西以查看是否应显示路线。属性定义如下:

public class SwaggerTagAttribute : Attribute
{
    public string[] Tags { get; private set; }

    public SwaggerTagAttribute(params string[] tags)
    {
        this.Tags = tags;
    }
}

这里定义了检测属性的IOperationFilter和去除路径的IDocumentFilter

public class RemoveTaggedOperationsFilter : IOperationFilter, IDocumentFilter
{
    private List<string> TagsToHide;

    public RemoveTaggedOperationsFilter()
    {
        TagsToHide = ConfigurationManager.AppSettings["TagsToHide"].Split(',').ToList();
    }

    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var tags = apiDescription.ActionDescriptor
            .GetCustomAttributes<SwaggerTagAttribute>()
            .Select(t => t.Tags)
            .FirstOrDefault();

        if (tags != null && TagsToHide.Intersect(tags).Any())
        {
            operation.tags = new List<string> {"Remove Me "};
        }
    }

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        foreach (var value in swaggerDoc.paths.Values)
        {
            if (value.post != null && value.post.tags.Contains("Remove Me"))
                value.post = null;

            if (value.get != null && value.get.tags.Contains("Remove Me"))
                value.get = null;

            if (value.put != null && value.put.tags.Contains("Remove Me"))
                value.put = null;

            if (value.delete != null && value.delete.tags.Contains("Remove Me"))
                value.delete = null;
        }
    }
}

并注册为:

 GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                {
                    c.OperationFilter<RemoveTaggedOperationsFilter>();
                    c.DocumentFilter<RemoveTaggedOperationsFilter>();
                });

我觉得在我之前可以访问的情况下标记要稍后删除的内容效率低下而且很老套。有什么方法可以让我从 IOperationFilter.Apply 中删除路由,而不是等待 IDocumentFilter 并扫描它?

早先有人 post 回答过,并说一旦有机会他们就会 post 编码。他们出于某种原因删除了他们的答案,但这让我找到了更好的解决方案。

与其使用 IOperationFilter 标记路由然后 IDocumentFilter 删除路由,不如使用 IDocumentFilter 找到自定义属性并一举删除它.代码如下:

public class HideTaggedOperationsFilter : IDocumentFilter
{
    private List<string> TagsToHide;

    public HideTaggedOperationsFilter()
    {
        TagsToHide = ConfigurationManager.AppSettings["TagsToHide"].Split(',').ToList();
    }

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        if (_tagsToHide == null) return;

        foreach (var apiDescription in apiExplorer.ApiDescriptions)
        {
            var tags = apiDescription.ActionDescriptor
                .GetCustomAttributes<SwaggerTagAttribute>()
                .Select(t => t.Tags)
                .FirstOrDefault();

            if (tags == null || !_tagsToHide.Intersect(tags).Any())
                continue;

            var route = "/" + apiDescription.Route.RouteTemplate.TrimEnd('/');
            swaggerDoc.paths.Remove(route);
        }
    }
}

public class SwaggerTagAttribute : Attribute
{
    public string[] Tags { get; }

    public SwaggerTagAttribute(params string[] tags)
    {
        this.Tags = tags;
    }
}

注册 IDocumentFilter:

GlobalConfiguration.Configuration.EnableSwagger(c =>
{
    ...
    c.DocumentFilter<HideTaggedOperationsFilter>();
});

然后像这样装饰一条路线:

 [SwaggerTag("MobileOnly")]
 public IHttpActionResult SendTest(Guid userId)
 {
    return OK();
 }

Edit: SwashBuckle 的 GitHub 页面上存在一些问题 post,建议在 swaggerDoc.path 中将每个 HTTP 谓词设置为 null 19=]。我发现这会破坏很多自动代码生成器,例如 AutoRest,所以我只是简单地删除了整个路径。 (看起来也比较简洁)