Support multiple versions of ASP.NET Core Web API

From Logic Wiki
Jump to: navigation, search


Let’s create an ASP.NET Core web API application. Open Project.json and include following nuget package.

"Microsoft.AspNetCore.Mvc.Versioning": "1.0.3"

Once the package is restored, we need to configure it. Next open Startup.cs, add the highlighted lines of code in ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();
    services.AddApiVersioning(option =>
    {
        option.ReportApiVersions = true;
        option.AssumeDefaultVersionWhenUnspecified = true;
        option.DefaultApiVersion = new ApiVersion(1, 0);
    });
}

Alternative to this I also saw the configuration below

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddApiVersioning(o => o.ApiVersionReader = new HeaderApiVersionReader("api-version"));
}

As you can see, there are 3 different options configured.

ReportAPIVersions: This is optional. But when set to true, API returns supported versions information in the response header.

AssumeDefaultVersionWhenUnspecified: This option will be used to serve the request without a version. The assumed API version by default would be 1.0.

DefaultApiVersion: This option is used to specify the default API version to be used when no version is specified in the request. This will default the version to 1.0. That’s all for the configuration and setup. Now we will see different ways of accessing the versions of the API.

Via Query String

Open the controller class, then decorate the controller class with ApiVersion attribute. Like,

namespace MultipleAPIVersions.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok(new string[] { "value1" });
    }
}

Here, the version is mentioned as 1.0. You can also create another controller class with the same name in a different namespace with API version set to 2.0. Like,

namespace AspNetCoreWebApi.Controllers2
{
    [ApiVersion("2.0")]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok(new string[] { "value2" });
    }
}

That’s all. Now go to browser and access the controller. You should see the output from API version 1.0 controller as it is set to default. Now append ?api-version=2 in the URL and you should see output from API version 2.0 controller.

Via URL Path Segment

Query string parameters are useful, but it can be painful in case of long URL and other query string parameters. Instead, the better approach would be to add version in the URL path. Like,

api/v1/values
api/v2/values

So to do this, we need to put the version in the route attribute. Like,

namespace MultipleAPIVersions.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok(new string[] { "value1" });
    }
}

Similarly, you will need to update route parameters to at all applicable places. With this change, the API endpoints always need to have the version number. You can navigate to version 1.0 via api/v1/values and to access version 2.0, change the version number in the URL. Simple and looks more clean now.

Ignoring API Versioning for some endpoints

If you have some APIs that you don’t want any versioning for them and you will only have one version of them, you can opt out using [ApiVersionNeutral] attribute like the following:

[ApiVersionNeutral]
[Route("api/optout")]
public class OptOutControler : Controller
{
	[HttpGet]
	public string Get() => HttpContext.GetRequestedApiVersion().ToString();
}

Via HTTP Headers

In both the above approaches, the URL needs to be modified to support versioning. But if you want your API URL’s to stay clean, then the API version information can also be passed via appending an HTTP Header. For this to work, you need to configure ApiVersionReader option.

services.AddApiVersioning(option =>
{
    option.ReportApiVersions = true;
    option.ApiVersionReader = new HeaderApiVersionReader("api-version");
    option.DefaultApiVersion = new ApiVersion(1, 0);
    option.AssumeDefaultVersionWhenUnspecified = true;
});

The highlighted line tells that header “api-version” is now where the API version number is expected. Make sure that Route attribute didn’t have version details. So test it, open Postman to add the header.

When you supply 2.0 as value to “api-version”, it calls the Version 2.0 controller and returns the output.

Simple and easy to set up. However, now the query string parameter will not work. Once you set the header, you can’t specify a query string parameter. If you wish to support both then instead of HeaderApiVersionReader, use QueryStringOrHeaderApiVersionReader. Like,

option.ApiVersionReader = 
         new QueryStringOrHeaderApiVersionReader()
{
      HeaderNames = { "api-version", "x-ms-version" }
};

So now, both query string parameter and header are supported. The default query string parameter name is api-version, so you can leave the constructor empty, but if you want another name, then you will need to supply. You can also have different names for query string parameter and for the header.

option.ApiVersionReader = 
         new QueryStringOrHeaderApiVersionReader(parameterName: "version")
{
      HeaderNames = { "api-version", "x-ms-version" }
};

MapToApiVersion

MapToApiVersion attribute allows to map a single API action to any version. In other words, a single controller which supports multiple versions say 1 and 3. The controller may have an API action method supported by version 3 only. In such case, you can use MapToApiVersion. Take a look at below code.

namespace MultipleAPIVersions.Controllers
{
    [ApiVersion("1.0")]
    [ApiVersion("3.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok(new string[] { "value1" });

        [HttpGet, MapToApiVersion("3.0")]
        public IActionResult GetV3() => Ok(new string[] { "value3" });
    }
}

Deprecated

When multiple API versions are supported, some versions will eventually be deprecated over time. To mark one or more API versions have been deprecated, simply decorate your controller with the deprecated API versions. This doesn’t mean that the API version is not supported. One can still call the endpoint/version. It just a way to make API users aware that following version will be deprecated in future.

[ApiVersion("1.0", Deprecated = true)]

ApiVersionNeutral

ApiVersionNeutral attribute defines that this API is version-neutral. This is useful for APIs that behaves the exact same way, regardless of API version or a legacy API that doesn’t support API versioning. So, you can add ApiVersionNeutral attribute to opt out from versioning.

[ApiVersionNeutral]
[RoutePrefix( "api/[controller]" )]
public class SharedController : Controller
{
    [HttpGet]
    public IActionResult Get() => Ok();
}

Access Version Information

If you wish to know which version client is trying to access, then you can get that information from,

public string Get() => HttpContext.GetRequestedApiVersion().ToString();



See original page http://www.talkingdotnet.com/support-multiple-versions-of-asp-net-core-web-api/

See Also https://koukia.ca/api-versioning-in-asp-net-core-2-0-1b55970aa29d