【问题标题】:NSwag multiple document endpointNSwag 多文档端点
【发布时间】:2021-02-23 17:19:38
【问题描述】:

可以像 swashbuckle 那样有多个文档端点吗?

options.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "API v2");

如果是,如何修饰 api 调用,使一些属于一个版本,一些属于另一个版本?

所以根据 Rico Suter 的建议,我所做的有点像这样:

ApiVersionAttribute.cs

public class ApiVersionAttribute:Attribute
    {
        private List<string> _versions = new List<string>();

        public List<string> Versions { get { return _versions; } }

        public ApiVersionAttribute(string version) {
            Versions.Add(version);
        }
    }

MyApiVersionProcessor.cs

        public string Version { get; set; }

        public MyApiVersionProcessor(string version)
        {
            this.Version = version;
        }

        public new Task<bool> ProcessAsync(OperationProcessorContext context)
        {
            bool returnValue = true;

            var versionAttributes = context.MethodInfo.GetCustomAttributes()
                .Concat(context.MethodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes())
                .Where(a => a.GetType()
                                .IsAssignableTo("MapToApiVersionAttribute", TypeNameStyle.Name) 
                            || a.GetType()
                                .IsAssignableTo("ApiVersionAttribute", TypeNameStyle.Name)
                       )
                .Select(a => (dynamic)a)
                .ToArray();

            var versionAttribute = versionAttributes.FirstOrDefault();

            if (null == versionAttribute)
            {
                returnValue = false;
            }
            else
            {
                if (ObjectExtensions.HasProperty(versionAttribute, "Versions")
                    && Version.Equals(versionAttribute.Versions[0].ToString()))
                {
                    ReplaceApiVersionInPath(context.OperationDescription, versionAttribute.Versions);
                }
                else {
                    returnValue = false;
                }
            }

            return Task.FromResult(returnValue);
        }

        private void ReplaceApiVersionInPath(SwaggerOperationDescription operationDescription,
            dynamic versions)
        {
            operationDescription.Path = operationDescription.Path.Replace("{version:apiVersion}",
                versions[0].ToString());
        }
    }

在我的 Global.asax 中

                // NSwag
                // https://github.com/RSuter/NSwag/wiki/OwinGlobalAsax#integration                
                app.UseSwaggerUi(typeof(WebApiApplication).Assembly, new SwaggerUiSettings
                {                       
                    //TypeNameGenerator = new MyTypeNameGenerator(),
                    MiddlewareBasePath = "/swagger",
                    SwaggerRoute = "/swagger/v1/swagger.json",
                    Version = "1.0.0.0",
                    // https://github.com/RSuter/NSwag/wiki/Middlewares
                    OperationProcessors =
                    {
                        new MyApiVersionProcessor("v1")
                    },
                    PostProcess = document =>
                    {                        
                        document.BasePath = "/";
                        document.Produces
                            = new List<string> { "application/json"
                                                , "text/json"
                                                , "text/html"
                                                , "plain/text"
                                                , "application/xml"};
                        document.Consumes
                            = document.Produces;
                        document.Info.Title = "Document V1";                     
                    }

                });

                app.UseSwaggerUi(typeof(WebApiApplication).Assembly, new SwaggerUiSettings
                {
                    //TypeNameGenerator = new MyTypeNameGenerator(),
                    MiddlewareBasePath = "/swagger",
                    SwaggerRoute = "/swagger/v2/swagger.json",
                    Version = "2.0.0.0",
                    OperationProcessors =
                    {
                        new MyApiVersionProcessor("v2")
                    },
                    PostProcess = document =>
                    {
                        document.BasePath = "/";
                        document.Produces
                            = new List<string> { "application/json"
                                                , "text/json"
                                                , "text/html"};
                        document.Consumes
                            = document.Produces;
                        document.Info.Title = "Document V2";
                    }

                });

并用

装饰我的控制器的方法
[ApiVersion("v2")]

[ApiVersion("v1")]

等等

【问题讨论】:

    标签: nswag


    【解决方案1】:

    您可以定义app.UseSwagger 两次并实现一个自定义操作处理器,该处理器根据您的需要仅过滤所需的api 操作(即在第一次调用中您应该过滤所有版本x,然后在第二次调用中过滤所有版本y)。

    目前默认添加的ApiVersionProcessor只是将路由路径中的版本占位符替换为第一个声明的版本。我认为我们应该扩展这个处理器,以便您可以排除版本并插入正确的版本。

    顺便说一句:我是 NSwag 的作者。

    【讨论】:

    • 我已经用您提出的解决方案修改了我的问题。
    • 顺便说一句:我刚刚更新了提交 github.com/RSuter/NSwag/commit/… 中的默认 ApiVersionProcessor,现在它可以开箱即用(请参阅单元测试示例)。这是你需要的吗?
    • 有点像,是的。我需要根据版本进行 api 过滤的多端点。路径中的版本信息是一个加号。
    【解决方案2】:

    现在有一个开箱即用的解决方案。我为 ASP.NET WebAPI Owin 提出了它,我想它在 ASP.NET Core 中应该非常相似。

    首先:您需要安装 ASP.NET API 版本控制(GitHubNuget

    第二:您需要用所需的路线和版本来装饰您的操作方法。例如:

    [Route("api/v{version:apiVersion}/get1")]
    [ApiVersion("1")]
    
    public IEnumerable<string> Get1()
    {           
       return new string[] { "value1", "value2" };
    }
    
    [Route("api/v{version:apiVersion}/get2")]
    [ApiVersion("2")]
    
    public IEnumerable<string> Get2()
    {           
       return new string[] { "value1", "value2" };
    }
    

    第三:您必须将所需的配置添加到 Startup.cs 文件中以 1) 为每个 API 版本生成单独的 OpenAPI 规范文件 2) 要求 NSwag 通过 Swagger 显示规范文件-界面

    public class Startup
    {
        [Obsolete]
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
    
            // register versioning
            config.MapHttpAttributeRoutes(new DefaultInlineConstraintResolver
            {
                ConstraintMap = { ["apiVersion"] = typeof(ApiVersionRouteConstraint) }
            });
    
            config.AddApiVersioning(options =>
            {
                // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
                options.ReportApiVersions = true;
    
                // automatically applies an api version based on the name of the defining controller's namespace
                options.Conventions.Add(new VersionByNamespaceConvention());
            });
    
            // generate OpenAPI Sepecification for each version and assign a route to it
    
            var assembly = typeof(Startup).Assembly;
    
            app.UseSwagger(assembly ,s =>
            {
                s.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "1" };
                s.GeneratorSettings.SchemaType = SchemaType.OpenApi3;
                s.DocumentPath = "/swagger/v1.0.json";
            });
    
            app.UseSwagger(assembly , s =>
            {
                s.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "2" };
                s.GeneratorSettings.SchemaType = SchemaType.OpenApi3;
    
                s.DocumentPath = "/swagger/v2.0.json";
            });
    
            // integrate Swagger-UI with the generated OpenAPI files generated before.
            _ = app.UseSwaggerUi3(assembly , s =>
              {
                  s.SwaggerRoutes.Add(new SwaggerUi3Route("Version 1", "/swagger/v1.0.json"));
                  s.SwaggerRoutes.Add(new SwaggerUi3Route("Version 2", "/swagger/v2.0.json"));
    
                  s.GeneratorSettings.Title = "My API";
                  s.GeneratorSettings.Description = "API functionalities.";
              });
    
            app.UseWebApi(config);
    
            config.EnsureInitialized();
        }
    }
    

    转到 Swagger 页面。你会看到:

    1. 右上角有一个下拉框,您可以选择版本
    2. 对于每个 API 版本,只有相关的操作方法是 列出。

    【讨论】:

      【解决方案3】:

      我们遇到了这个问题,但我们正计划反过来做。

      1. 卷曲招摇文档
      2. 翻遍并根据标签拆分
      3. 为每个标签创建本地 swagger 文档
      4. 对每个新创建的 swagger 文档运行 nswag cli

      这样我们就不必仅仅因为客户端需要其他东西而改变服务器。

      (如果我们成功了会更新:D)


      编辑:所以我已经成功地将架构分解为多个架构,每个控制器一个,并通过 nswag 生成新文件。 不是最漂亮的代码,但它可以工作。如果有人感兴趣,可以在 github 上发布它

      【讨论】:

      • 我现在意识到,当我为类似的问题漫不经心时,我可能评论了错误的问题......
      猜你喜欢
      • 1970-01-01
      • 2020-12-03
      • 2021-03-28
      • 1970-01-01
      • 2018-11-09
      • 1970-01-01
      • 1970-01-01
      • 2021-01-15
      • 2020-09-29
      相关资源
      最近更新 更多