10

I'd like to prepare my .NET Core Web API project so that multiple versions of the API can be managed and documented, according to the REST services standards.

I'm using .NET Core 2.1 with NSwag (v11.18.2). I also installed the Microsoft.AspNetCore.Mvc.Versioning NuGet package.

I already searched with Google for some configuration examples, but the only useful link I found is this.

I'm now able to get Swagger pages for both API versions but with some problems:

  1. Please note that none of the last config settings (Title, Description, etc.) takes effect on any of the 2 routes. It only works if I add them on each of the individual configuration. So I'd also like to know if it possible to avoid that, since the general configuration of the API can be version indipendent (title, description and so on...).
  2. Since the issue with NSwag and Microsoft API Versioning package discussed in the above link, was opened 2-3 months (and NSwag versions too) ago, I'd like to know if it is now truly fixed and in this case, which is the right configuration to set.
  3. Although the version is explicit in the configuration of the controllers, it is still required as a mandatory input parameter of the controller methods and of course I don't want that! See image:

Swagger UI tests requiring version as input parameter for methods

So, my actual configuration, by following that example, is looking like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddApiVersioning(options =>
        {
            options.AssumeDefaultVersionWhenUnspecified = true;
            options.DefaultApiVersion = new ApiVersion(1, 0);
            options.ReportApiVersions = true;
        });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseSwaggerWithApiExplorer(config =>
    {
        config.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "1.0" };
        config.SwaggerRoute = "v1.0.json";
    });

    app.UseSwaggerWithApiExplorer(config =>
    {
        config.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "2.0" };
        config.SwaggerRoute = "v2.0.json";
    });

    app.UseSwaggerUi3(typeof(Startup).GetTypeInfo().Assembly, config =>
    {
        config.SwaggerRoutes.Add(new SwaggerUi3Route("v1.0", "/v1.0.json"));
        config.SwaggerRoutes.Add(new SwaggerUi3Route("v2.0", "/v2.0.json"));

        config.GeneratorSettings.Title = "My API";
        config.GeneratorSettings.Description = "API functionalities.";
        config.GeneratorSettings.DefaultUrlTemplate = "{v:apiVersion}/{controller}/{action}/{id?}";
        config.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase
    });
}

And these are my actual controllers:

[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
[SwaggerTag("Test1", Description = "Core operations on machines (v1.0).")]
public class MachinesController : Controller
{
    [HttpGet("{id}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<ActionResult<Machine>> Get(int id)
    {
        return await ...
    }
}

[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
[SwaggerTag("Test2", Description = "Core operations on machines (v2.0).")]
public class MachinesController : Controller
{
    [HttpGet("{id}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<ActionResult<Machine>> Get(int id)
    {
        return await ...
    }
}
Cheshire Cat
  • 1,941
  • 6
  • 36
  • 69

2 Answers2

3
  1. They are ignored in the middleware because they are inferred from the settings or do not apply for api explorer (template). However title and description should work...
  2. Please create an issue with the specific issue and a repro, also check out the existing tests in the repo
  3. Fixed with v11.18.3
Rico Suter
  • 11,548
  • 6
  • 67
  • 93
  • **Problem n. 3**: fixed! Thank you! **Problem n. 2**: I opened an Issue [here](https://github.com/RSuter/NSwag/issues/1524) but just for a suggestion. Hope it can be useful somehow! **Problem n. 1**: still there and title and description don't work. What do you mean when you say that "_they are ignored in the middleware because they are inferred from the settings..._"? – Cheshire Cat Aug 09 '18 at 07:34
  • 1
    DefaultUrlTemplate is not supported by API Explorer because you have to specify routes for every operation otherwise it's not picked up by API Explorer. DefaultPropertyNameHandling is ignored and read from the Newtonsoft.Json serializer settings (you dont have to set it yourself). Title and description must be a bug, can you create an issue for that too? – Rico Suter Aug 09 '18 at 08:12
  • Thanks for hints. Added new [issue](https://github.com/RSuter/NSwag/issues/1525). – Cheshire Cat Aug 09 '18 at 08:27
3

I believe starting in NSwag 12.0.0, there is significantly improved support for the API Explorer. It's important that the complementary API Explorer package for API versioning is also referenced so that the proper information is provided to NSwag.

The Swagger sample application provided by API Versioning uses Swashbuckle, but the setup will be very similar to NSwag. You can use the IApiVersionDescriptionProvider service to enumerate all of the API versions defined in your application. That should significantly simplify your NSwag configuration.

You're versioning by URL segment; therefore, to address Problem 3 you simply need to configure the API Explorer a la:

services.AddVersionedApiExplorer( options => options.SubstituteApiVersionInUrl = true );

This will replace the {version} route parameter in the route template with the corresponding API version value and remove the API version parameter from the API description.

Chris Martinez
  • 3,185
  • 12
  • 28
  • Hi Chris, can you please provide a code sample of using the IApiVersionDescriptionProvider? I got my setup to work, but I am adding the documents manually using services.AddSwaggerDocument(), but this involve adding a document per version manually. I know how to add the routes using IApiVersionDescriptionProvider, but that does not add the actual documents? as per https://github.com/RicoSuter/NSwag/issues/1760 @Rico Suter states "UseSwaggerUi3() only configures the UI and the document to use". How can I automatically add the documents? – Codendaal May 26 '19 at 08:58
  • Did you go the **Swagger sample application** in the link I provided? The exact place that sample is here: https://github.com/Microsoft/aspnet-api-versioning/blob/master/samples/aspnetcore/SwaggerSample/Startup.cs#L87. The **IApiVersionDescriptionProvider** is provided by API Versioning. You request it directly through DI or implicitly though DI composition. This service will give you the metadata information for the Swagger endpoints to add including: API version, group name, and whether the version is deprecated. – Chris Martinez May 26 '19 at 22:23
  • The API Explorer will collate APIs by their **GroupName** which is the API version formatted the way you want and is usually how it will be displayed in the Swagger endpoint route. I know Swashbuckle uses the **GroupName** to group actions when generating Swagger documents. I'm not sure about NSwag. If it doesn't do it automatically, you could do it yourself pretty easily with a LINQ query that groups on that property. – Chris Martinez May 26 '19 at 22:28
  • If I do 'services.AddSwaggerDocument();' in "ConfigureServices()" and then use IApiVersionDescriptionProvider in "Configure()" to add the routes like "config.SwaggerRoutes.Add(new SwaggerUi3Route($"v{description.GroupName}", $"/v{description.GroupName}.json"));" Only "/swagger/v1/swagger.json" works and it contains all methods (v1 & v2). Do you want me to open an issue so I can provide a code sample? – Codendaal May 27 '19 at 02:27
  • Hi Chris. Sorry, I was being an idiot....I managed to get it working by getting the Interface in ConfigureServices() by using DI "var provider = services.BuildServiceProvider().GetRequiredService();" – Codendaal May 27 '19 at 03:08
  • For those who are trying to add the versioning to the NSwag documentation using services.AddVersionedApiExplorer() you should install Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer – Andre Batista Aug 03 '20 at 23:42