It's possible to have multiple document endpoint like in swashbuckle?
options.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "API v2");
If Yes, how to decorate api calls so that some belongs to one versione and some to the other version?
So According to Rico Suter suggestion what I've done it's kinda like that:
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());
}
}
And in my 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";
}
});
And decorated my Controllers' Methods with
[ApiVersion("v2")]
[ApiVersion("v1")]
etc.
You can define app.UseSwagger twice and implement a custom operation processor which filters only the desired api actions according to your needs (ie in the first call you should filter allversion x and in the second all versions of y).
The ApiVersionProcessor which is currently added by default only replaces the version placeholder in the route path with the first declared version. I think we should extend this processor so that you can exclude versions and also that the correct version is inserted.
Btw: I'm the author of NSwag.
There is an out of the box solution now. I propose it for ASP.NET WebAPI Owin and I guess it should be very similar in ASP.NET Core.
First: You need to install ASP.NET API Versioning (GitHub, Nuget)
Second: You need to decorate your action methods with the desired route and version. For example:
[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" };
}
Third: You got to add the required configuration to the Startup.cs file to 1) Generate a separate OpenAPI Specification file for each API version 2) Ask NSwag to show the sepecification files through 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();
}
}
Go to the Swagger page. You'll See:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With