Web API – Versioning

In this post, we will see how to manage the versioning in a .net core Web API service, using three different approaches:
1) Query String-Based Versioning
2) URL-Based Versioning
3) HTTP Header-Based Versioning

First of all, we open Visual Studio 2019 and we create a Web API project:


If we run the application, this will be the output:


Now, in order to manage the versioning, we have to install the library
Microsoft.AspNetCore.Mvc.Versioning
and then, we have to modify the method ConfigureServices in the startup file:

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




QUERY STRING-BASED VERSIONING:
We open the file WeatherForecastController and we create two versions of the controller:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

namespace WebAPIVersioning.Controllers
{
    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/Forecast")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [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();
        }
    }


    [ApiController]
    [ApiVersion("2.0")]
    [Route("api/Forecast")]
    public class WeatherForecastControllerVersion2 : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastControllerVersion2(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [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();
        }
    }
}


In order to call the different versions of the controller, we have to pass the version of controller in the query string:



URL-BASED VERSIONING:
We open the file WeatherForecastController and we create two versions of the controller:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

namespace WebAPIVersioning.Controllers
{
    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/v{v:apiVersion}/Forecast")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [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();
        }
    }


    [ApiController]
    [ApiVersion("2.0")]
    [Route("api/v{v:apiVersion}/Forecast")]
    public class WeatherForecastControllerVersion2 : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastControllerVersion2(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [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();
        }
    }
}


In order to call the different versions of the controller, we have to put the version of controller in an URL path segment:




HTTP HEADER-BASED VERSIONING:
We open the file WeatherForecastController and we create two versions of the controller:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

namespace WebAPIVersioning.Controllers
{
    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/Forecast")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [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();
        }
    }


    [ApiController]
    [ApiVersion("2.0")]
    [Route("api/Forecast")]
    public class WeatherForecastControllerVersion2 : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastControllerVersion2(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [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();
        }
    }
}


Finally, we have to modify the method ConfigureServices, in the the startup file:

[startup.cs]

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddApiVersioning(o => 
    {
         o.AssumeDefaultVersionWhenUnspecified = true;
         o.DefaultApiVersion = new ApiVersion(1, 0);
         o.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
    });
}



Now, we run the application using Postman and we put in the header the controller’s version: