NSwag 多文档端点
NSwag multiple document endpoint
是否可以像 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")]
等等
您可以定义 app.UseSwagger
两次并实现一个自定义操作处理器,该处理器仅根据您的需要过滤所需的 api 操作(即在第一次调用中您应该过滤所有版本 x,在第二次调用中y 的所有版本)。
目前默认添加的ApiVersionProcessor
只是将路由路径中的版本占位符替换为第一个声明的版本。我认为我们应该扩展此处理器,以便您可以排除版本并插入正确的版本。
顺便说一句:我是 NSwag 的作者。
现在有开箱即用的解决方案。我为 ASP.NET WebAPI Owin 建议它,我想它在 ASP.NET Core 中应该非常相似。
首先: 你需要安装ASP.NET API Versioning (GitHub, Nuget)
其次:您需要使用所需的路由和版本来装饰您的操作方法。例如:
[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 版本 2) 要求 NSwag 通过 Swagger-UI
显示规范文件
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 页面。你会看到:
- 右上角有一个下拉框,可以让您 select 版本
- 对于每个API版本,只有相关的操作方法是
列出。
我们已经 运行 解决了这个问题,但我们正计划以相反的方式解决这个问题。
- 卷曲 swagger 文档
- 翻阅它并根据标签拆分它
- 为每个标签创建本地 swagger 文档
- 运行 针对每个新创建的 swagger 文档的 nswag cli
这样我们就不必因为客户端需要其他东西而改变服务器。
(如果我们成功了会更新 :D)
编辑:所以我已经成功地将模式分解为多个模式,每个控制器一个,并通过 nswag 生成了新文件。
不是最漂亮的代码,但它可以工作。如果有人感兴趣,可以在 github 上发布它
是否可以像 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")]
等等
您可以定义 app.UseSwagger
两次并实现一个自定义操作处理器,该处理器仅根据您的需要过滤所需的 api 操作(即在第一次调用中您应该过滤所有版本 x,在第二次调用中y 的所有版本)。
目前默认添加的ApiVersionProcessor
只是将路由路径中的版本占位符替换为第一个声明的版本。我认为我们应该扩展此处理器,以便您可以排除版本并插入正确的版本。
顺便说一句:我是 NSwag 的作者。
现在有开箱即用的解决方案。我为 ASP.NET WebAPI Owin 建议它,我想它在 ASP.NET Core 中应该非常相似。
首先: 你需要安装ASP.NET API Versioning (GitHub, Nuget)
其次:您需要使用所需的路由和版本来装饰您的操作方法。例如:
[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 版本 2) 要求 NSwag 通过 Swagger-UI
显示规范文件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 页面。你会看到:
- 右上角有一个下拉框,可以让您 select 版本
- 对于每个API版本,只有相关的操作方法是 列出。
我们已经 运行 解决了这个问题,但我们正计划以相反的方式解决这个问题。
- 卷曲 swagger 文档
- 翻阅它并根据标签拆分它
- 为每个标签创建本地 swagger 文档
- 运行 针对每个新创建的 swagger 文档的 nswag cli
这样我们就不必因为客户端需要其他东西而改变服务器。
(如果我们成功了会更新 :D)
编辑:所以我已经成功地将模式分解为多个模式,每个控制器一个,并通过 nswag 生成了新文件。 不是最漂亮的代码,但它可以工作。如果有人感兴趣,可以在 github 上发布它