如何使用 Swashbuckle 从关于 WebAPI 的 Swagger 文档中省略方法

Posted

技术标签:

【中文标题】如何使用 Swashbuckle 从关于 WebAPI 的 Swagger 文档中省略方法【英文标题】:How to omit methods from Swagger documentation on WebAPI using Swashbuckle 【发布时间】:2015-06-24 10:40:30 【问题描述】:

我有一个 C# ASP.NET WebAPI 应用程序,其中 API 文档是使用 Swashbuckle 自动生成的。我希望能够从文档中省略某些方法,但我似乎无法弄清楚如何告诉 Swagger 不要将它们包含在 Swagger UI 输出中。

我觉得这与添加模型或模式过滤器有关,但不清楚该怎么做,文档似乎只提供了如何修改方法输出的示例,而不是将其从输出中完全删除。

【问题讨论】:

【参考方案1】:

您可以在使用文档过滤器生成 swagger 文档后从它中删除“操作” - 只需将动词设置为 null(不过,也可能有其他方法)

以下示例仅允许使用 GET 动词 - 并取自 this issue。

class RemoveVerbsFilter : IDocumentFilter

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    
        foreach (PathItem path in swaggerDoc.paths.Values)
        
            path.delete = null;
            //path.get = null; // leaving GET in
            path.head = null;
            path.options = null;
            path.patch = null;
            path.post = null;
            path.put = null;
        
    

在你的招摇配置中:

...EnableSwagger(conf => 

    // ...

    conf.DocumentFilter<RemoveVerbsFilter>();
);

【讨论】:

请注意:即使您取消注释 path.get = null;,这也不会删除路径——因此这些路径仍将包含在 Swagger 文件中,但只是没有详细信息。正如您在 GitHub 上的原始回复中提到的那样,在您的答案中包含 ApiExplorerSettingsAttribute 可能会更好。使用 ApiExplorerSettings 还可以避免将类型信息添加到 Swagger 文件的 schemes 列表中。【参考方案2】:

我希望完全删除路径项的字典条目:

var pathsToRemove = swaggerDoc.Paths
                .Where(pathItem => !pathItem.Key.Contains("api/"))
                .ToList();

foreach (var item in pathsToRemove)

    swaggerDoc.Paths.Remove(item.Key);

使用这种方法,您不会在生成的 swagger.json 定义中获得“空”项目。

【讨论】:

【参考方案3】:

您可以将以下属性添加到 Controllers 和 Actions 以将它们从生成的文档中排除:[ApiExplorerSettings(IgnoreApi = true)]

【讨论】:

工作得很好,这应该是答案 有没有办法以编程方式做到这一点?根据配置设置,我想在某些环境中公开 API,但在其他环境中不公开。 Swashbuckle 文档:Omit Arbitrary Operations System.Web.Http.Description.ApiExplorerSettings, System.Web.Http 它仍然可以工作 asp.net mvc int dot net-framework :)【参考方案4】:

有人在 github 上发布了解决方案,所以我将其粘贴在这里。所有的功劳都归他所有。 https://github.com/domaindrivendev/Swashbuckle/issues/153#issuecomment-213342771

首先创建一个属性类

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class HideInDocsAttribute : Attribute


然后创建一个文档过滤器类

public class HideInDocsFilter : IDocumentFilter

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    
        foreach (var apiDescription in apiExplorer.ApiDescriptions)
        
            if (!apiDescription.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<HideInDocsAttribute>().Any() && !apiDescription.ActionDescriptor.GetCustomAttributes<HideInDocsAttribute>().Any()) continue;
            var route = "/" + apiDescription.Route.RouteTemplate.TrimEnd('/');
            swaggerDoc.paths.Remove(route);
        
    

然后在 Swagger Config 类中,添加那个文档过滤器

public class SwaggerConfig

    public static void Register(HttpConfiguration config)
    
        var thisAssembly = typeof(SwaggerConfig).Assembly;

        config
             .EnableSwagger(c =>
                
                    ...                       
                    c.DocumentFilter<HideInDocsFilter>();
                    ...
                )
            .EnableSwaggerUi(c =>
                
                    ...
                );
    

最后一步是在你不希望 Swashbuckle 生成文档的 Controller 或 Method 上添加 [HideInDocsAttribute] 属性。

【讨论】:

我认为 RemoveRoute 可能是我正在寻找的机器人。【参考方案5】:

基于@spottedmahns answer。 我的任务是相反的。只显示那些被允许的。

框架:.NetCore 2.1;招摇:3.0.0

添加属性

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ShowInSwaggerAttribute : Attribute


并实现自定义IDocumentFilter

public class ShowInSwaggerFilter : IDocumentFilter

    public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    

        foreach (var contextApiDescription in context.ApiDescriptions)
        
            var actionDescriptor = (ControllerActionDescriptor) contextApiDescription.ActionDescriptor;

            if (actionDescriptor.ControllerTypeInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any() ||
                actionDescriptor.MethodInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any())
            
                continue;
            
            else
            
                var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
                var pathItem = swaggerDoc.Paths[key];
                if(pathItem == null)
                    continue;

                switch (contextApiDescription.HttpMethod.ToUpper())
                
                    case "GET":
                        pathItem.Get = null;
                        break;
                    case "POST":
                        pathItem.Post = null;
                        break;
                    case "PUT":
                        pathItem.Put = null;
                        break;
                    case "DELETE":
                        pathItem.Delete = null;
                        break;
                

                if (pathItem.Get == null  // ignore other methods
                    && pathItem.Post == null 
                    && pathItem.Put == null 
                    && pathItem.Delete == null)
                    swaggerDoc.Paths.Remove(key);
            
        
    

配置服务代码:

public void ConfigureServices(IServiceCollection services)

     // other code

    services.AddSwaggerGen(c =>
    
        // other configurations
        c.DocumentFilter<ShowInSwaggerFilter>();
    );

【讨论】:

谢谢阿莱哈。这种方法实际上适用于 ApiExplorerSettingsAttribute 不起作用的 SwashBuckle.OData。【参考方案6】:

制作过滤器

public class SwaggerTagFilter : IDocumentFilter

    public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    
        foreach(var contextApiDescription in context.ApiDescriptions)
        
            var actionDescriptor = (ControllerActionDescriptor)contextApiDescription.ActionDescriptor;
            
            if(!actionDescriptor.ControllerTypeInfo.GetCustomAttributes<SwaggerTagAttribute>().Any() && 
               !actionDescriptor.MethodInfo.GetCustomAttributes<SwaggerTagAttribute>().Any())
            
                var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
                swaggerDoc.Paths.Remove(key);
            
        
    

创建一个属性

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class SwaggerTagAttribute : Attribute


在startup.cs中应用

services.AddSwaggerGen(c => 
    c.SwaggerDoc(1, new Info  Title = "API_NAME", Version = "API_VERSION" );
    c.DocumentFilter<SwaggerTagFilter>(); // [SwaggerTag]
);

将 [SwaggerTag] 属性添加到要包含在 Swagger JSON 中的方法和控制器

【讨论】:

甜蜜。适当的方法,感谢您分享 sln。【参考方案7】:

在 SwaggerConfig 中添加一行

c.DocumentFilter<HideInDocsFilter>();

...

public class HideInDocsFilter : IDocumentFilter

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
     
        var pathsToRemove = swaggerDoc.Paths
            .Where(pathItem => !pathItem.Key.Contains("api/"))
            .ToList();
    
        foreach (var item in pathsToRemove)
        
            swaggerDoc.Paths.Remove(item.Key);
        
    

【讨论】:

【参考方案8】:

可能对某人有所帮助,但在开发(调试)期间,我们喜欢公开整个控制器和/或操作,然后在生产期间隐藏它们(发布构建)

#if DEBUG
    [ApiExplorerSettings(IgnoreApi = false)]
#else
    [ApiExplorerSettings(IgnoreApi = true)]
#endif  

【讨论】:

【参考方案9】:

与@aleha 一样,我想在默认情况下排除,这样我就不会意外暴露端点(默认情况下是安全的),但我使用的是使用 OpenApiDocument 的更新版本的 Swagger。

创建 ShowInSwagger 属性

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ShowInSwaggerAttribute : Attribute

然后创建一个文档过滤器

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System;
using System.Linq;
using TLS.Common.Attributes;

namespace TLS.Common.Filters

    public class ShowInSwaggerFilter : IDocumentFilter
    
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        
            foreach (var contextApiDescription in context.ApiDescriptions)
            
                var actionDescriptor = (ControllerActionDescriptor)contextApiDescription.ActionDescriptor;

                if (actionDescriptor.ControllerTypeInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any() ||
                    actionDescriptor.MethodInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any())
                
                    continue;
                
                else
                
                    var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
                    var operation = (OperationType)Enum.Parse(typeof(OperationType), contextApiDescription.HttpMethod, true);

                    swaggerDoc.Paths[key].Operations.Remove(operation);

                    // drop the entire route of there are no operations left
                    if (!swaggerDoc.Paths[key].Operations.Any())
                    
                        swaggerDoc.Paths.Remove(key);
                    
                
            
        
    

然后在您的 startup.cs 或 ConfigureServices 中:

public void ConfigureServices(IServiceCollection services)

     // other code

    services.AddSwaggerGen(c =>
    
        c.DocumentFilter<ShowInSwaggerFilter>();
        // other config
    );

【讨论】:

【参考方案10】:

您可以在控制器和方法级别创建自定义过滤器。因此,任何具有您的属性的控制器/方法都将在 Swagger 文档中可用。此过滤器还从您的文档中删除了重复的 HTTP 动词(在此示例中,我仅将其用于 GET/PUT/POST/PATCH),但是,您始终可以根据您的要求进行自定义

属性

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class PublicApi:Attribute



文档过滤器

public class PublicApiFilter : IDocumentFilter

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    

        var publicPaths = new List<string> "/api";

        var publicApiDescriptions = new List<ApiDescription>();

        var publicMethods = FilterByPublicControllers(swaggerDoc, apiExplorer, publicPaths, publicApiDescriptions);

        FilterByPublicActions(swaggerDoc, publicApiDescriptions, publicMethods);
    

    private static Dictionary<string, List<string>> FilterByPublicControllers(SwaggerDocument swaggerDoc, IApiExplorer apiExplorer, List<string> publicPaths, List<ApiDescription> publicApiDescriptions)
    
        var publicMethods = new Dictionary<string, List<string>>();
        foreach (var apiDescription in apiExplorer.ApiDescriptions)
        
            var isPublicApiController = apiDescription.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<PublicApi>().Any();
            var isPublicApiMethod = apiDescription.ActionDescriptor.GetCustomAttributes<PublicApi>().Any();


            if (!isPublicApiController && !isPublicApiMethod)
            
                continue;
            

            var relativePath = ToRelativePath(apiDescription);

            publicPaths.Add(relativePath);
            publicApiDescriptions.Add(apiDescription);

            var action = apiDescription.ActionDescriptor.ActionName;
            List<string> available = null;
            if (!publicMethods.TryGetValue(relativePath, out available))
                publicMethods[relativePath] = new List<string>();
            publicMethods[relativePath].Add(action);
        

        swaggerDoc.paths = swaggerDoc.paths.Where(pair => publicPaths.Contains(pair.Key))
            .ToDictionary(pair => pair.Key,
                pair => pair.Value);
        return publicMethods;
    

    private static void FilterByPublicActions(SwaggerDocument swaggerDoc, List<ApiDescription> publicApis, Dictionary<string, List<string>> publicMethods)
    
        foreach (var api in publicApis)
        
            var relativePath = ToRelativePath(api);
            var availableActions = publicMethods[relativePath];
            if (availableActions == null)
            
                continue;
            

            foreach (var path in swaggerDoc.paths.Where(pair => pair.Key.IndexOf(relativePath) > -1).ToList())
            
                if (!availableActions.Contains("Get"))
                    path.Value.get = null;
                if (!availableActions.Contains("Post"))
                    path.Value.post = null;
                if (!availableActions.Contains("Put"))
                    path.Value.put = null;
                if (!availableActions.Contains("Patch"))
                    path.Value.patch = null;
            
        
    

    private static string ToRelativePath(ApiDescription apiDescription)
    
        return "/" + apiDescription.RelativePath.Substring(0,apiDescription.RelativePath.LastIndexOf('/'));
    

最后,注册你的 SwaggerConfig

public class SwaggerConfig

    public static void Register()
    

        var thisAssembly = typeof(SwaggerConfig).Assembly;
        GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                
                    c.SingleApiVersion("v1", "Reports");
                    c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
                    c.DocumentFilter<PublicApiFilter>();
                )
            .EnableSwaggerUi(c =>
                

                );

    

示例

控制器

[PublicApi]
public class ProfileController : ApiController

方法

 public class UserController : ApiController
 
    [PublicApi]
    public ResUsers Get(string sessionKey, int userId, int groupId) 
        return Get(sessionKey, userId, groupId, 0);
    

【讨论】:

以上是关于如何使用 Swashbuckle 从关于 WebAPI 的 Swagger 文档中省略方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Swashbuckle.AspNetCore 隐藏响应代码 200?

使用 Swashbuckle V5 从代码生成 swagger.json

如何使用 React JS 将 WEBA 或 Opus 音频文件转换为 Mp3?

如何在使用 swashbuckle 的 swagger 的发布请求描述中隐藏属性?

Swashbuckle.AspNet.Core:Swagger UI 显示空白页面 - 如何修复?

如何使用 Swashbuckle.AspNetCore 在 Swagger 模式中将自定义泛型类型公开为字符串