Sunday, June 16, 2019

[ASP.Net Core] API Versioning


 ASP.NET Core   Web API   Versioning  




▌Introduction


This article shows how to support API versioning on ASP.NET Core Web API.



▌Environment

▋.NET Core 2.2.104
▋Visual  Studio 2017 Community



▌Implement


▋Install Packages


▋Enable Versioning

In Startup.cs,

public void ConfigureServices(IServiceCollection services)
{
  // ApiVersioning
  services.AddApiVersioning(opt =>
  {
     opt.ReportApiVersions = true; // List supported versons on Http header
     opt.DefaultApiVersion = new ApiVersion(1, 0); // Set the default version
     opt.AssumeDefaultVersionWhenUnspecified = true; // Use the api of default version
     opt.ApiVersionSelector = new CurrentImplementationApiVersionSelector(opt); // Use the api of latest release number
  });
}


Results:

HTTP/1.1 200 OK
api-supported-versions: 1.0


The DefaultApiVersion can has a postfix like this,

opt.DefaultApiVersion = new ApiVersion (2, 1, "awesome");

Which will results in the header of http response:
HTTP/1.1 200 OK
api-supported-versions: 2.1-awesome


▋Multiple version for same API

We can have the API in 2 versions with different Action names.

[HttpGet("Get")]
[ApiVersion("1.0")]
public ActionResult<IEnumerable<string>> Get()
{
    return new string[] { "Version", "v1.0" };
}

[HttpGet("Get")]
[ApiVersion("2.0")]
public ActionResult<IEnumerable<string>> GetV2()
{
    return new string[] { "Version", "v2.0" };
}

Or create them within different namespaces with the same Action name.


▋Request without specified version

▋Request without specified version

Any http requests without specified version will use the API of default version.

Notice if no API matches the default version specified at

1.  opt.DefaultApiVersion = new ApiVersion (...);
2.  [ApiVersion("...")]

An Bad Request(400) will be returned like following,

HTTP/1.1 400 Bad Request
api-supported-versions: 1.0, 2.0

{"error":{"code":"UnsupportedApiVersion","message":"The HTTP resource that matches the request URI 'http://localhost:5000/api/Demo/Get' is not supported.","innerError":null}}



▋By URI Parameter

Use the following Uri parameter to specify the target version:
api-version=<version_number>

For example,
http://localhost:5000/api/Demo/Get?api-version=1
http://localhost:5000/api/Demo/Get?api-version=2
http://localhost:5000/api/Demo/Get?api-version=2.0


▋By Routing

[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class DemoController : ControllerBase
{ }

Usage:
http://localhost:5000/api/v1/Demo/Get
http://localhost:5000/api/v2.0/Demo/Get


▋By HTTP Header

Update the ConfigureServices in Startup.cs as following,

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


We can specify more headers like this,

opt.ApiVersionReader = new HeaderApiVersionReader("api-version","apver");

An HTTP header example of a request:

GET <http://localhost:5000/api/Demo/Get> HTTP/1.1
Host: localhost:5000
api-version: 2.0


▋By both header or uri parameter

public void ConfigureServices(IServiceCollection services)
{
    services.AddApiVersioning(opt =>
    {
        opt.ReportApiVersions = true;
        opt.DefaultApiVersion = new ApiVersion(1, 0);       
        opt.AssumeDefaultVersionWhenUnspecified = true;
        opt.ApiVersionReader = ApiVersionReader.Combine(
            new QueryStringApiVersionReader("ver"),
            new HeaderApiVersionReader("api-version")
        );
    });
}


▋Multiple version for non-modified APIs

[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/[controller]")]
[ApiController]
public class DemoController : ControllerBase
{
    [HttpGet("Get")]
    [MapToApiVersion("1.0")]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "Version", "v1.0" };
    }

    [HttpGet("Get")]
    [MapToApiVersion("2.0")]
    public ActionResult<IEnumerable<string>> GetV2()
    {
        return new string[] { "Version", "v2.0" };
    }

    //This is a non-modified action, available for versions 1.0/2.0`
    [HttpPost("Post")]
    public ActionResult<IEnumerable<string>> Post()
    {
        return new string[] { "Version", "all" };
    }
}

▋Demo : For non-modified API

The requests,
http://localhost:5000/api/Demo/Post
http://localhost:5000/api/Demo/Post?api-verson=1
http://localhost:5000/api/Demo/Post?api-version=2.0

Will get the same response as following,
HTTP/1.1 200 OK
api-supported-versions: 1.0, 2.0

["Version","all"]



▋Deprecated version

[ApiVersion("1.0", Deprecated=true)]
[ApiVersion("2.0")]
[ApiVersion("3.0")]
[Route("api/[controller]")]
[ApiController]
public class DemoController : ControllerBase
{
    //...
}


▋Demo : Deprecated information on Http response

HTTP/1.1 200 OK
api-supported-versions: 2.0, 3.0
api-deprecated-versions: 1.0

["Version","v2.0"]



▋Neutral API

For those APIs that are not going to be version controlled, use [ApiVersionNeutral] to enable all versions available for them.

▋For all APIs in a ApiController

[ApiVersionNeutral]
[Route("api/[controller]")]
[ApiController]
public class DemoNeutralController : ControllerBase
{  }


▋For a single API(Action)

[ApiVersionNeutral]
[HttpGet("Get")]
public ActionResult<IEnumerable<string>> Get()
{
    return new string[] { "Version", HttpContext.GetRequestedApiVersion().ToString() };
}




▌Reference





No comments:

Post a Comment