.NET Core 3.1 Web API - Keeping Different API Versions In A Single Code Base

Use Case

You want to upgrade your existing API with a new version and some customers can't accommodate those new APIs. Customers who can't accommodate the latest APIs should be able to use the old versions and customers who can accommodate the latest API version should able to use the latest one. So a single code base here should host both versions.

NuGet Dependencies

You need to add the below dependencies in your Web API project 
  1. Microsoft.AspNetCore.Mvc.Versioning - A service API versioning library for Microsoft ASP.NET Core
  2. Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer - Discovering metadata such as the list of API-versioned controllers and actions, and their URLs and allowed HTTP methods
  3. Swashbuckle.AspNetCore - Swagger tools for documenting APIs built on ASP.NET Core

Code Changes in Controller

Attributes for Controller Class
The below attributes show that versions 1 and 2 are supported by the controller class and also define a route including the version number 
  1. [ApiVersion("1")]  
  2.     [ApiVersion("2")]  
  3.     [ApiController]  
  4.     [Route("api/v{version:apiVersion}/[controller]")]  
  5.     public class WeatherForecastController : ControllerBase  
  6.     {  
Attributes for Actions
Below MapToApiVersion attribute is showing what version number each of the actions supports.
  1. [HttpGet]    
  2. [MapToApiVersion("1")]    
  3. [Route("Weather")]    
  4. public IEnumerable<WeatherForecast> GetWeatherV1()    
  5. {    
  8. [HttpGet]    
  9. [MapToApiVersion("2")]    
  10. [Route("Weather")]    
  11. public IEnumerable<WeatherForecast> GetWeatherV2()    
  12. {    

Custom Classes for Swagger

Below classes are defined with swagger configuration and swagger default behavior.
  1. ConfigureSwaggerOptions
    This class has been derived from IConfigureOptions<SwaggerGenOptions> and defines the basic configuration for Swagger to add documents for each API version

  2. SwaggerDefaultValues
    This class has been derived from IOperationFilter and is adding support to load swagger documents based on the version selected

Code Changes in Startup.cs

Modify ConfigureServices()

The below lines will be added to ConfigureServices() method to initialize API Version & API Version Explorer libraries and also to register the above-mentioned custom classes. Please notice that we made version 1 as the default version.
  1. services.AddApiVersioning(options =>  
  2.             {  
  3.                 options.DefaultApiVersion = new ApiVersion(1, 0);  
  4.                 options.AssumeDefaultVersionWhenUnspecified = true;  
  5.                 options.ReportApiVersions = true;  
  6.             });  
  8. services.AddVersionedApiExplorer(options =>  
  9.             {  
  10.                 // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service  
  11.                 // note: the specified format code will format the version as "'v'major[.minor][-status]"  
  12.                 options.GroupNameFormat = "'v'VVV";  
  14.                 // note: this option is only necessary when versioning by url segment. the SubstitutionFormat  
  15.                 // can also be used to control the format of the API version in route templates  
  16.                 options.SubstituteApiVersionInUrl = true;  
  17.             });  
  18.             services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();  
  19.             services.AddSwaggerGen(options => options.OperationFilter<SwaggerDefaultValues>());  

Modify Configure()
The below code will be adding Swagger and SwaggerUI support. It also defines the swagger UI end points to browse for each API version. 
  1. // Enable middleware to serve generated Swagger as a JSON endpoint.  
  2.             app.UseSwagger();  
  4.             // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),  
  5.             // specifying the Swagger JSON endpoint.  
  6.             app.UseSwaggerUI(  
  7.             options =>  
  8.             {  
  9.                 // build a swagger endpoint for each discovered API version  
  10.                 foreach (var description in provider.ApiVersionDescriptions)  
  11.                 {  
  12.                     options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());  
  13.                 }  
  14.             });  

Run Web API with Swagger UI as Default

Let us run the application after modifying the  below line inside launchSettings.json, which will make swagger as the default UI for the Web API project.
  1. "WeatherWebAPI": {  
  2.        "commandName""Project",  
  3.        "launchBrowser"true,  
  4.        "launchUrl""swagger",  
  5.        "environmentVariables": {  
  6.            "ASPNETCORE_ENVIRONMENT""Development"  
  7.        },  
  8.        "applicationUrl""https://localhost:5001;http://localhost:5000"  
  9.    },  
You will notice that default version 1 swagger has been loaded and once it executes  this version from the UI itself, you will get the response as expected below.
.NET Core 3.1 Web API - Keeping Different API Versions In A Single Code Base
Now change the version from the UI itself as below and notice that the entire swagger definition has been changed to version 2. Once executing, you will get the ex[ected response from version 2 of the API.
.NET Core 3.1 Web API - Keeping Different API Versions In A Single Code Base
.NET Core 3.1 Web API - Keeping Different API Versions In A Single Code Base
.NET Core 3.1 Web API - Keeping Different API Versions In A Single Code Base
This is the way to host multiple versions of APIs in a single code base. I uploaded the source code here for you to play around with. You can remove the docker file in it, if you have issues with docker and just try to run it locally.