ASP.NET Core Web API Versioning 的做法

ASP.NET Core Web API 版本(Versioning)的做法有很多種,

在 URL 上面、QueryString 的參數,或是在 Header 中。

本文就來看看 ASP.NET Core Web API 多版本的做法。

本文參考自「Support multiple versions of ASP.NET Core Web API

當新增好 ASP.NET Core Web API 專案後,預設有一個 Values 的 API 可以測試,

以下我們就一步步的來看看 API 版本的各種做法。

1.加入 Microsoft.AspNetCore.Mvc.Versioning 套件

 

2.在 Statrup.cs 檔案的 ConfigureServices Method中設定 Versioning

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

在 AddApiVersioning 中有3個設定值,

ReportApiVersions: 如果這個設 true 的話,就會將支援的版本列在 header 之中。
DefaultApiVersion: 設定 API 預設的版本。 
AssumeDefaultVersionWhenUnspecified: 設個設 true 的話,使用時如果沒特別指定的話,就會用 DefaultApiVersion 設定的版本。

所以我們 GET /api/values 回傳的 Header 中就會有 api-supported-versions 這個內容,如下,

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Kestrel
api-supported-versions: 1.0

註: ReportApiVersions 預設值為 fase 。

3.建立不同版本的 API 

3.1.舊有的 api 設定版本為 1.0,在 ValuesController Class上設定 ApiVersion 屬性,如下

namespace WebAPIVersionDemo.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "api v1" };
        }
    }
}

3.2.新增一個 version 2.0 的 Controller (不同 namespace 相同 Controller name)

namespace WebAPIVersionDemo.ControllersV2
{
    [ApiVersion("2.0")]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] {"api v2"};
        }
    }
}

3.3.預設是 Version 1.0 ,內容如下,

4.透過 QueryString 指定呼叫的 Version

4.1.呼叫 2.0 (?api-version=2)

4.2.呼叫 1.0 (?api-version=1)

註:如果給不同的版本會發生錯誤哦! 如下,

5.在 URL Route 中指定,例如 api/v1/values or api/v2/values 

5.1.設定 Controller 的 Route 屬性,來 Support

namespace WebAPIVersionDemo.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "api v1" };
        }
    }
}

6.透過 HTTP Headers 指定呼叫的 Version

6.1.先將原本在 Controller 上的 Route 屬性從 Route("api/v{version:apiVersion}/[controller]") 改回原本的 Route("api/[controller]")

6.2.在 Statrup.cs 檔案的 ConfigureServices Method中設定 從 Header 讀取 Versioning 的資訊,如下,

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

6.3.所以在header 中加入 api-version:1 or api-version:2 就可以呼叫想要的版本,如下,

7.透過 HTTP Headers 或 QueryString 都可指定呼叫的 Version

如果想要讓 HTTP Headers 或 QueryString 都可指定呼叫的 Version 的話,可以在 Statrup.cs 檔案的 ConfigureServices Method中設定。將 HeaderApiVersionReader 改成 QueryStringOrHeaderApiVersionReader ,如下,

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

這樣子的話,如果 Header 給 api-version:1 或是 x-ms-version:2 都可以。

因為 QueryStringOrHeaderApiVersionReader 沒有傳入參數名稱,所以 QueryString 預設值為 api-version,所以使用上跟 上述第4點一樣。

如果要改 QueryString 的參數名稱可以在建立 QueryStringOrHeaderApiVersionReader 時指定,例如設定為 v ,如下

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

8.MapToApiVersion

有時候 API 升級並不是整個都重寫,如果在原本的 Controller 之中擴充的話,

就可以在 Controller 多加入 ApiVersion 的宣告,如下加入 V3.0 ,

namespace WebAPIVersionDemo.Controllers
{
    [ApiVersion("1.0")]
    [ApiVersion("3.0")]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "api v1" };
        }
    }
}

這時如果在原本的Controller中 v3.0 要用另一個 Method 實作的話,就可以使用 MapToApiVersion 去設定,如下,

namespace WebAPIVersionDemo.Controllers
{
    [ApiVersion("1.0")]
    [ApiVersion("3.0")]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "api v1" };
        }

        [HttpGet, MapToApiVersion("3")]
        public IEnumerable<string> GetV3()
        {
            return new string[] { "api v3" };
        }
    }
}

9.Deprecated 

如果有些 api 將來要刪掉的話,可加入 deprecated 來讓使用的人知道,如下,

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

所以在 Headers 中就會有 api-deprecated-versions 值為 1.0, api-supported-versions 值就變成了 2.0, 3.0 ,如下,

10.ApiVersionNeutral

如果我們再新增一個 RMController (GetRequestedApiVersion Method,可以取得 Client Request 的版本號),如下,

namespace WebAPIVersionDemo.ControllersNeutral
{
    [Route("api/[controller]")]
    public class RMController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { $"api v {HttpContext.GetRequestedApiVersion()}" };
        }
    }
}

因為沒有指定版本號,而 ValuesController 中的版本號有 1 ~ 3,所以我們可以呼叫 RMController 並指定版本號為 1 ~ 3。

如果指定版本4的話,就會發生錯誤。

如果想要讓這個 Controller 不管什麼版本都可以 Work 的話,就可以多設定 ApiVersionNeutral 這個屬性,如下,

namespace WebAPIVersionDemo.ControllersNeutral
{
    [ApiVersionNeutral]
    [Route("api/[controller]")]
    public class RMController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { $"api v {HttpContext.GetRequestedApiVersion()}" };
        }
    }
}

參考資料

Support multiple versions of ASP.NET Core Web API

ASP.NET Core RESTful Web API versioning made easy

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^