Creating .NET Core API and Swagger UI with versioning

Hendi Suhardja
5 min readJun 2, 2020

--

One of the major challenges surrounding exposing services is handling updates to the API contract. Clients may not want to update their applications when the API changes, so a versioning strategy becomes crucial. A versioning strategy allows clients to continue using the existing REST API and migrate their applications to the newer API when they are ready.

One way to version a REST API is to include the version number in the URI path.

This solution uses URI routing to point to a specific version of the API. Because cache keys (in this situation URIs) are changed by version, clients can easily cache resources. When a new version of the REST API is released, it is perceived as a new entry in the cache.

First let’s create .NET Core API with versioning

.NET Core API with versioning

First, create new project “ASP.NET Core Web Application” then choose tempate “API”

Run the project, it will open the browser and display json data, and see the url of the API, it doesn’t have version number

Now let’s add versioning on the API

  • Open Package Manager, find “Microsoft.AspNetCore.Mvc.Versioning” and “Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer” then click install
  • On controller, above controller name, add below attributes
[ApiVersion("1.0")][Route("v{version:apiVersion}/[controller]")]
  • On startup class, add below codes on ConfigureServices method
services.AddApiVersioning(options =>{// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"options.ReportApiVersions = true;});services.AddVersionedApiExplorer(options =>{// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service// note: the specified format code will format the version as "'v'major[.minor][-status]"options.GroupNameFormat = "'v'VVV";// note: this option is only necessary when versioning by url segment. the SubstitutionFormat// can also be used to control the format of the API version in route templatesoptions.SubstituteApiVersionInUrl = true;});
  • Now let’s try adding same endpoint with different version, you could do this by adding new method on controller with attribute [ApiVersion(“{versionNumber”)]
  • Now our controller will have 2 methods
[HttpGet]public IEnumerable<WeatherForecast> Get(){var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();}
[HttpGet][ApiVersion("2.0")]public IEnumerable<WeatherForecast> GetV2(){var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(1, 10),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();}

Now let’s create Swagger UI

Swagger UI with versioning

  • First open package manager, find “Swashbuckle.AspNetCore” then click install
  • On root of project, add new class “ConfigureSwaggerOptions”

Note : you could define your swagger title on “Title” field

public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>{readonly IApiVersionDescriptionProvider provider;/// <summary>/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class./// </summary>/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;/// <inheritdoc />public void Configure(SwaggerGenOptions options){// add a swagger document for each discovered API version// note: you might choose to skip or document deprecated API versions differentlyforeach (var description in provider.ApiVersionDescriptions){try{options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));}catch (Exception){}}}static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description){var info = new OpenApiInfo(){Title = "Versioning API",Version = description.ApiVersion.ToString()};if (description.IsDeprecated){info.Description += " This API version has been deprecated.";}return info;}}
  • On root of project add new class “SwaggerDefaultValues”
public class SwaggerDefaultValues : IOperationFilter{/// <summary>/// Applies the filter to the specified operation using the given context./// </summary>/// <param name="operation">The operation to apply the filter to.</param>/// <param name="context">The current operation filter context.</param>public void Apply(OpenApiOperation operation, OperationFilterContext context){var apiDescription = context.ApiDescription;operation.Deprecated |= apiDescription.IsDeprecated();if (operation.Parameters == null){return;}foreach (var parameter in operation.Parameters){var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);if (parameter.Description == null){parameter.Description = description.ModelMetadata?.Description;}parameter.Required |= description.IsRequired;}}}
  • On Startup class, add below codes above constructor
static string XmlCommentsFileName{get{var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml";return fileName;}}
  • On Startup, at method ConfigureServices, add below codes
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();services.AddSwaggerGen(options =>{// add a custom operation filter which sets default valuesoptions.OperationFilter<SwaggerDefaultValues>();// integrate xml commentsoptions.IncludeXmlComments(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), XmlCommentsFileName));options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme{In = ParameterLocation.Header,Description = "Please insert JWT with Bearer into field",Name = "Authorization",Type = SecuritySchemeType.ApiKey});options.AddSecurityRequirement(new OpenApiSecurityRequirement {{new OpenApiSecurityScheme{Reference = new OpenApiReference{Type = ReferenceType.SecurityScheme,Id = "Bearer"}},new string[] { }}});});
  • On Startup class change method Configure into
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthorization();// Swaggerapp.UseSwagger();app.UseSwaggerUI(options =>{// build a swagger endpoint for each discovered API versionforeach (var description in provider.ApiVersionDescriptions){options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());}});app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}
  • Then right click on project on Solution explorer, choose Properties, navigate to tab Build, check “XML documentation file” then press Ctrl+S
  • Now run the project, then on browser navigate to localhost:port/swagger. In my case : https://localhost:44398/swagger , It will display Swagger UI with versioning number

That’s all, thanks for reading.

You could find source code on https://github.com/hendisuhardja/NETCoreAPIVersionAndSwaggerUI

--

--

Responses (1)