Versioning in asp.net core web api

There has been lot of interesting and heated debate on how you should version your api’s. In this article, we will mainly focus on query string based versioning, header based versioning and url based versioning. 

After creating a asp.net core web api project, add Microsoft.AspNetCore.Mvc.Versioning from the nuget package

Now, modify your startup class to support versioning. 

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddApiVersioning();
        }

After running the application, you will receive and error stating- An API version is required, but was not specified

Now, amend the AddApiVersioning method

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

Default version to be set as 1.0, when the version is not specified. 

Query string based versioning

I have created two controller, one as HelloWorldController and other one as HelloWorld2Controller but the route url of both are same. 

    [Route("api/[controller]")]
    [ApiController]
    [ApiVersion("1.0")]
    [ApiVersion("1.1")]
    public class HelloWorldController : ControllerBase
    {
        // GET: api/values
        [HttpGet]
        public string Get()
        {
            return "Hello world";
        }

        [HttpGet,MapToApiVersion("1.1")]
        public string Get11()
        {
            return "Hello world v1.1";
        }
    }

I have set the route of HelloWorld2Controller to api/HelloWorld which is same as our HelloWorldController.

    [Route("api/HelloWorld")]
    [ApiController]
    [ApiVersion("2.0")]
    public class HelloWorld2Controller : ControllerBase
    {
        // GET: api/values
        [HttpGet]
        public string Get()
        {
            return "Hello World 2";
        }
    }

ApiVersion is used to represent the metadata that describes the api version associated with the service; whereas, MapToVersion is used to map the method to specific version of the api.

After running the application, call gets default to HelloWorldController’s Get method because we are setting the unspecified version to 1.0.

Now pass the api-version query string to the url, in my case its https://localhost:5001/api/Helloworld?api-version=2. Now it should point to version 2 of the api i.e. HelloWorld2Controller. 

Assume, you want to change the name of query string  from api-version to version. You can do it by setting ApiVersionReader to QueryStringApiVersionReader in the configure services method.

services.AddApiVersioning(setupAction =>
            {
                setupAction.AssumeDefaultVersionWhenUnspecified = true;
                setupAction.DefaultApiVersion = new ApiVersion(1, 0);
                 setupAction.ApiVersionReader = new QueryStringApiVersionReader(new[] { "version" });
           });

Url Based approach

Honestly, I’m not a big fan of url based approach due to explicitly versioning in the route attribute; moreover, we cannot use it with other versioning approaches. 

    [Route("api/v{version:apiversion}/HelloWorld")]
    [ApiController]
    [ApiVersion("2.0")]
    public class HelloWorld2Controller : ControllerBase
    {
    }
    [Route("api/v{version:apiversion}/[controller]")]
    [ApiController]
    [ApiVersion(“1.0")]
    [ApiVersion("1.1")]
    public class HelloWorldController : ControllerBase
    { 
    }

Now run the application with url https://localhost:5001/api/v1.1/Helloworld

Header based versioning

Another approach of using versioning is by using header based versioning. Most of the developers advocates this approach because unlike the URL path param and query string approach, using request header doesn’t require fiddling around with the URLs on the client side. The downside to using request headers for versioning is that the versioning option is not explicitly visible to the client. 

We can configure the header based versioning approach by setting the ApiVersionReader to HeaderApiVersionReader in the startup class. 

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //services.AddApiVersioning();
            services.AddApiVersioning(setupAction =>
            {
                setupAction.AssumeDefaultVersionWhenUnspecified = true;
                setupAction.DefaultApiVersion = new ApiVersion(2, 0);
                setupAction.ReportApiVersions = true;
                //setupAction.ApiVersionReader = new HeaderApiVersionReader(new[] { "x-ver" });
            });
        }

Support multiple versioning

Supporting multiple versioning can provide flexibility and give options to the clients/developers to use versioning based on their choice. Multiple versioning can be used by using combine method of static class ApiVersionReader. 

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //services.AddApiVersioning();
            services.AddApiVersioning(setupAction =>
            {
                setupAction.AssumeDefaultVersionWhenUnspecified = true;
                setupAction.DefaultApiVersion = new ApiVersion(2, 0);
                setupAction.ApiVersionReader = ApiVersionReader.Combine(
                    new HeaderApiVersionReader(new[] { "x-ver" }),
                     new QueryStringApiVersionReader(new[] { "version" }));
            });
        }

Adversing the versioning

We can advertise the versioning of api’s by using ReportApiVersionsin the startup file. 

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //services.AddApiVersioning();
            services.AddApiVersioning(setupAction =>
            {
                setupAction.AssumeDefaultVersionWhenUnspecified = true;
                setupAction.DefaultApiVersion = new ApiVersion(2, 0);
                setupAction.ReportApiVersions = true;
                setupAction.ApiVersionReader = ApiVersionReader.Combine(
                    new HeaderApiVersionReader(new[] { "x-ver" }),	
                     new QueryStringApiVersionReader(new[] { "version" }));
            });
        }

Advertising the depreciated versions

Similarly like advertising, we can also advertise the depreciated version by setting depreciated property in ApiVersion attribute

    [Route("api/[controller]")]
    //[Route("api/v{version:apiversion}/[controller]")]
    [ApiController]
    [ApiVersion("1.0",Deprecated =true)]
    [ApiVersion("1.1")]
    public class HelloWorldController : ControllerBase
    {
        // GET: api/values
        [HttpGet]
        public string Get()
        {
            return "Hello world";
        }

        [HttpGet,MapToApiVersion("1.1")]
        public string Get11()
        {
            return "Hello world v1.1";
        }
    }

I hope you like the article

One thought on “Versioning in asp.net core web api

  1. Pingback: Versioning in asp.net core web api | Anup1252000's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s