如何實現 ASP.NET Core WebApi 的版本化
Web API 的版本化可以儘量保證在相同url情況下保留一個 api 的多個版本,通常一個 webapi 會有多個client,這些client包括:app,web,html5,crawl 等等同構或者異構的平臺,當 api 升級之後,往往升級前的 api 也得保留,當維護兩個api的時候就是一個不小的挑戰,畢竟還是存在一些 client 使用者需要訪問老的api,這時候就需要將 webapi 版本化。
安裝 Versioning 包
要想使用 webapi 的版本化功能,需要用 nuget 引用 Microsoft.AspNetCore.Mvc.Versioning
包,還可以通過 Visual Studio 2019 的 NuGet package manager
NuGet package manager
命令列工具輸入以下命令:
dotnet add package Microsoft.AspNetCore.Mvc.Versioning
啟動 API 版本化
當包成功新增到專案之後,接下來就可以在 Startup.ConfigureServices
中將 ApiVersioning 注入到容器中,如下程式碼所示:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddApiVersioning(); } }
在呼叫 AddApiVersioning()
的時候記得 using Microsoft.AspNetCore.Mvc.Versioning
,除了這個預設方法,還可以做一些全域性配置,如下程式碼所示:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddApiVersioning(v => { v.ReportApiVersions = true; v.AssumeDefaultVersionWhenUnspecified = true; v.DefaultApiVersion = new ApiVersion(1, 0); }); } }
使用 QueryString 指定版本號
先來看一下程式碼,考慮如下的 api。
[ApiController]
[ApiVersion("2.0")]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[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();
}
}
從程式碼中可以看到,我在 WeatherForecastController 上標記了該 Controller 是 v2.0 版本,接下來如何訪問呢? 可以通過如下連結: http://localhost:61582/api/weatherforecast?api-version=2.0
看到這裡,有些朋友就有疑問了,v2.0
的 Get 是有了,那 v1.0
的 Get 怎麼訪問呢? 實際開發中的做法是這樣的,會用兩個名稱空間來表示相應的版本號,可以看如下程式碼:
namespace WebApplication6.Controllers.v1
{
[ApiController]
[ApiVersion("1.0")]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
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();
}
}
}
namespace WebApplication6.Controllers.v2
{
[ApiController]
[ApiVersion("2.0")]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
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();
}
}
}
從上面程式碼可以看到 WebApplication6.Controllers.v1
表示版本 v1.0 , WebApplication6.Controllers.v2
表示版本 v2.0,接下來依次瀏覽這兩個url。
使用 routes 指定版本號
很顯然使用 QueryString 的方式不是很優雅也不符合 Restful 規範,接下來看一下如何通過 Route 改造,考慮下面的類,請注意我是如何在 route 中進行版本化的。
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[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();
}
}
在這裡,我將 [Route("api/[controller]")]
替換成了 [Route("api/v{version:apiVersion}/[controller]")]
,接下來將專案跑起來,如下圖所示:
忽略 API 版本
在 API 版本化時,有些 API 可能只有一個版本的需求,這時候可以使用 ApiVersionNeutral
特性來忽視版本化,如下程式碼所示:
[ApiVersionNeutral]
[Route("api/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[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();
}
}
譯文連結:https://www.infoworld.com/article/3433156/advanced-versioning-in-aspnet-core-web-api.html
更多高質量乾貨:參見我的 GitHub: csharptranslate