[英]NSwag: Generate C# Client from multiple Versions of an API
We are versioning our API and generating the Swagger specification using Swashbuckle in ASP.NET Core 1.1.我们正在对 API 进行版本控制,并使用 ASP.NET Core 1.1 中的 Swashbuckle 生成 Swagger 规范。 We can generate two API docs based on those JSON specification files:我们可以基于这些 JSON 规范文件生成两个 API 文档:
<!-- language: c# -->
services.AddSwaggerGen(setupAction =>
{
setupAction.SwaggerDoc("0.1", new Info { Title = "Api", Version = "0.1", Description = "API v0.1" });
setupAction.SwaggerDoc("0.2", new Info { Title = "Api", Version = "0.2", Description = "API v0.2" });
// more configuration omitted
}
We are including all actions in both spec files, unless it is mapped to a specific version using the [MapToApiVersion]
and ApiExplorerSettings(GroupName ="<version>")]
attributes.我们将所有操作都包含在两个规范文件中,除非它使用[MapToApiVersion]
和ApiExplorerSettings(GroupName ="<version>")]
属性映射到特定版本。 Methods belonging to an older version only are also decorated with the [Obsolete]
attribute:仅属于旧版本的方法也用[Obsolete]
属性修饰:
<!-- language: c# -->
[MapToApiVersion("0.1")]
[ApiExplorerSettings(GroupName = "0.1")]
[Obsolete]
However, we want to have only one C# Client generated from the Union of both spec files, where all methods are included in the Client, 0.1 as well as 0.2, but all obsolete methods marked, in fact, as obsolete.但是,我们只想从两个规范文件的联合生成一个 C# 客户端,其中所有方法都包含在客户端中,0.1 和 0.2,但实际上所有过时的方法都标记为过时。
I have looked into both NSwag (which we are using for quite some time now) as well as AutoRest.我已经研究了 NSwag(我们已经使用了很长一段时间)以及 AutoRest。 AutoRest seems to support a merging scenario , but I could not get it to work because of schema validation errors (and I am more than unsure whether our specific scenario would be actually supported). AutoRest 似乎支持合并场景,但由于模式验证错误,我无法让它工作(我非常不确定我们的特定场景是否会得到实际支持)。
My last idea as of now to get this sorted is to somehow JSON-merge the specs into one and then feed it to NSwag.到目前为止,我对这个进行排序的最后一个想法是以某种方式将规范 JSON 合并为一个,然后将其提供给 NSwag。
Do we miss anything here?我们在这里错过了什么吗? Is this somehow possible to realize with NSwag?这是否可以通过 NSwag 实现?
I wrote an article about similar problem https://medium.com/dev-genius/nswag-charp-client-from-multiple-api-versions-7c79a3de4622我写了一篇关于类似问题的文章https://medium.com/dev-genius/nswag-charp-client-from-multiple-api-versions-7c79a3de4622
First of all, create a schema.首先,创建一个模式。 As I see, there are two approaches:如我所见,有两种方法:
Next, create clients for each supported version and wrap them under the wrapper client:接下来,为每个支持的版本创建客户端并将它们包装在包装器客户端下:
public class AppApiClient
{
public IV1Client V1 { get; }
public IV2Client V2 { get; }
public AppApiClient(HttpClient httpClient)
{
V1 = new V1Client(httpClient);
V2 = new V2Client(httpClient);
}
}
Here is my idea, expanding from the comments:这是我的想法,从评论中扩展:
With swashbuckle you can generate as many SwaggerDoc as you like, the idea on this case is to generate 3 keep the same 2 versions that you have and add one more that will have everything.使用 swashbuckle,您可以根据需要生成任意数量的 SwaggerDoc,这种情况下的想法是生成 3 个与您拥有的相同的 2 个版本,并添加一个将拥有所有内容的版本。
c.MultipleApiVersions(
(apiDesc, targetApiVersion) =>
targetApiVersion.Equals("default") || // Include everything by default
apiDesc.Route.RouteTemplate.StartsWith(targetApiVersion), // Only include matching routes for other versions
(vc) =>
{
vc.Version("default", "Swagger_Test");
vc.Version("v1_0", "Swagger_Test V1_0");
vc.Version("v2_0", "Swagger_Test V2_0");
});
Here is a working sample:这是一个工作示例:
http://swagger-net-test-multiapiversions.azurewebsites.net/swagger/ui/index?filter=Api http://swagger-net-test-multiapiversions.azurewebsites.net/swagger/ui/index?filter=Api
And the entire code for that project is on GitHub:该项目的全部代码都在 GitHub 上:
https://github.com/heldersepu/Swagger-Net-Test/tree/MultiApiVersions https://github.com/heldersepu/Swagger-Net-Test/tree/MultiApiVersions
Packages:套餐:
Install-Package Swashbuckle.AspNetCore安装包 Swashbuckle.AspNetCore
Install-Package Microsoft.AspNetCore.Mvc.Versioning安装包 Microsoft.AspNetCore.Mvc.Versioning
ValueV1Controller.cs ValueV1Controller.cs
[ApiVersion("1")]
[Route("api/v{version:apiVersion}/Values")]
public class ValuesV1Controller : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
ValueV2Controller.cs ValueV2Controller.cs
[ApiVersion("2")]
[Route("api/v{version:apiVersion}/Values")]
public class ValuesV2Controller : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1.2", "value2.2" };
}
}
Startup.cs启动文件
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddApiVersioning();
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API - V1", Version = "v1" });
c.SwaggerDoc("v2", new Info { Title = "My API - V2", Version = "v2" });
c.DocInclusionPredicate((docName, apiDesc) =>
{
var versions = apiDesc.ControllerAttributes()
.OfType<ApiVersionAttribute>()
.SelectMany(attr => attr.Versions);
return versions.Any(v => $"v{v.ToString()}" == docName);
});
c.OperationFilter<RemoveVersionParameters>();
c.DocumentFilter<SetVersionInPaths>();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v2/swagger.json", "My API V2");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseMvc();
}
}
public class RemoveVersionParameters : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var versionParameter = operation.Parameters?.SingleOrDefault(p => p.Name == "version");
if (versionParameter != null)
operation.Parameters.Remove(versionParameter);
}
}
public class SetVersionInPaths : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Paths = swaggerDoc.Paths
.ToDictionary(
path => path.Key.Replace("v{version}", swaggerDoc.Info.Version),
path => path.Value
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.