2019年6月16日 星期日

[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





沒有留言:

張貼留言