Implementing Versioning in ASP.NET Core WebAPI

Introduction

In modern web development, maintaining compatibility and managing changes in APIs is crucial. Versioning allows developers to introduce new features, fix bugs, or make breaking changes without disrupting existing consumers. In this article, we will explore how to implement versioning in an ASP.NET Core WebAPI, providing a practical example along the way.

Why Versioning Matters?

Versioning enables developers to evolve their APIs over time while ensuring backward compatibility. It allows consumers to continue using older versions of the API while gradually transitioning to newer versions. With versioning, you can introduce breaking changes without impacting existing clients, making the overall development and deployment process more efficient.

Implementing Versioning in ASP.NET Core WebAPI

Let's dive into the step-by-step process of implementing versioning in an ASP.NET Core WebAPI.

Step 1. Create a New ASP.NET Core WebAPI Project

Start by creating a new ASP.NET Core WebAPI project in your preferred development environment, such as Visual Studio or Visual Studio Code. Ensure that you have the necessary dependencies installed, including the .NET Core SDK.

Step 2. Add Required NuGet Packages

To implement versioning, you need to add the appropriate NuGet packages to your project. Open the NuGet Package Manager Console and install the following packages.

Install-Package Asp.Versioning.Mvc
Install-Package Asp.Versioning.Mvc.ApiExplorer

These packages provide the necessary components and middleware to support versioning.

Step 3. Configure Versioning in Program.cs

In the Program.cs file, add the following code to configure versioning.

builder.Services
    .AddApiVersioning(options =>
    {
        options.DefaultApiVersion = new ApiVersion(1, 0);
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.ReportApiVersions = true;
    })
    .AddApiExplorer(options =>
    {
        options.GroupNameFormat = "'v'VVV";
        options.SubstituteApiVersionInUrl = true;
    });

This code sets the default API version to 1.0, assumes the default version when not specified explicitly, and enables reporting of supported API versions.

Step 4. Enable API Versioning in Controllers

In your controller classes, apply the [ApiVersion] attribute to indicate the supported versions. For example,

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
    // Controller actions and endpoints
}

This code specifies that the WeatherForecastController supports version 1.0 of the API.

Now add another version of the above controller:

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
    // Controller actions and endpoints
}

Now there are 2 versions of WeatherForecast API. But when we run the application and check the definition drop-down, only the definition of V1 is available and we are not able to find V2.

Step 5. Add all versioning to Swagger

Now create a new folder at the root of the project as Swagger and add a new file SwaggerDefaultValues.cs to the folder and copy the following codes:

public class SwaggerDefaultValues : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var apiDescription = context.ApiDescription;
        operation.Deprecated |= apiDescription.IsDeprecated();

        foreach (var responseType in context.ApiDescription.SupportedResponseTypes)
        {
            var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString();
            var response = operation.Responses[responseKey];

            foreach (var contentType in response.Content.Keys)
            {
                if (responseType.ApiResponseFormats.All(x => x.MediaType != contentType))
                {
                    response.Content.Remove(contentType);
                }
            }
        }

        if (operation.Parameters == null)
        {
            return;
        }

        foreach (var parameter in operation.Parameters)
        {
            var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);

            parameter.Description ??= description.ModelMetadata?.Description;

            if (parameter.Schema.Default == null && description.DefaultValue != null &&
                description.DefaultValue is not DBNull && description.ModelMetadata is ModelMetadata modelMetadata)
            {
                var json = JsonSerializer.Serialize(description.DefaultValue, modelMetadata.ModelType);
                parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json);
            }

            parameter.Required |= description.IsRequired;
        }
    }
}

Here we implemented IOperationFiler which iterates through all the available API versions.

Now create a another file ConfigureSwaggerOptions.cs in the Swagger folder and include the following code. This file enables Swashbuckle to understand and manage the API versions within the application.

public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider _provider;

    public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => _provider = provider;

    public void Configure(SwaggerGenOptions options)
    {
        foreach (var description in _provider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
        }
    }

    private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
    {
        var info = new OpenApiInfo()
        {
            Title = "Versioning Web API",
            Version = description.ApiVersion.ToString(),
            Description = "Description for the Versioning Web API"
        };

        if (description.IsDeprecated)
        {
            info.Description += " This API version has been deprecated.";
        }

        return info;
    }
}

Now in Program.cs change the Swagger registration and UseSwaggerUI configuration:

builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
builder.Services.AddSwaggerGen(options =>
{
    options.OperationFilter<SwaggerDefaultValues>();
});
app.UseSwaggerUI(options =>
{
    var descriptions = app.DescribeApiVersions();
    foreach (var description in descriptions)
    {
        var url = $"/swagger/{description.GroupName}/swagger.json";
        var name = description.GroupName.ToUpperInvariant();
        options.SwaggerEndpoint(url, name);
    }
});

Step 6. Test the API Versions

Test the application and explore the various API versions. Access all API version definitions conveniently positioned at the top-right section(Select a definition drop-down) of your page.

Conclusion

Versioning is a critical aspect of building and maintaining APIs. By implementing versioning in your ASP.NET Core WebAPI, you can manage changes, introduce new features, and maintain backward compatibility. This article provided a step-by-step guide to implementing versioning in ASP.NET Core WebAPI, empowering you to create robust and scalable APIs that meet the evolving needs of your consumers. Hope this article will help the readers.

Happy Coding !!!


Similar Articles