我们在进行webapi服务开发时,会遇到一些多个版本的api共存的情况发生,例如某一版本APP上线后,需求发生变更,需要在下一个升级版本更新API,但同时又需要保证这个APP版本能正常使用,这时候就需要采用API服务版本控制。
版本控制一般有以下几种方式:
- 在url上增加查询字符串参数的方式,追加版本,例如 api/service?v=2
- 在url路径上增加版本。例如:api/v2/service(这种方式个人认为目前是最优雅的方式)
- 在http请求head中加入版本标识
目前微软提供了 Microsoft.AspNetCore.Mvc.Versioning 组件,能够支持以上几种方式。
组件注册
services.AddApiVersioning(options => { options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; options.DefaultApiVersion = new ApiVersion(1, 0); });
- ReportAPIVersions: 这是可选的。但是, 当设置为 true 时, API 将返回响应标头中支持的版本信息。
- AssumeDefaultVersionWhenUnspecified: 此选项将用于不提供版本的请求。默认情况下, 假定的 API 版本为1.0。(这个经测试对url追加版本方式不起作用)
- DefaultApiVersion: 此选项用于指定在请求中未指定版本时要使用的默认 API 版本。这将默认版本为1.0。
几个重要的类
- ApiVersion 特性标识当前Controller的版本,注意路由的配置。这里我们采用了第 2 个版本控制方式
- MapToApiVersion 属性允许将单个 API 操作映射到某一版本
实现一个小需求:在同一个Controller里面新增一个api版本
http://localhost:5000/gateway/api/user/headclaims --->指向 默认v1版本
http://localhost:5000/gateway/api/v1/user/headclaims ---->指向 v1版本
http://localhost:5000/gateway/api/v2/user/headclaims ---->指向 v2版本
配置如下:
[ApiVersion("1.0")]
[ApiVersion("2.0")]
//多路由支持
[Route("api/v{version:apiVersion}/[controller]")]
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserAppService _userService;
[ApiVersion("2.0")]
//多路由支持
[Route("api/v{version:apiVersion}/[controller]")]
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserAppService _userService;
public UserController(IUserAppService userService)
{
_userService = userService;
}
{
_userService = userService;
}
[Route("headclaims")]
[HttpGet]
public IActionResult HeadClaims()
{
var claimTypes = new List<string> { "name", "phone", "userId", "introduce" };
var claimHeads = Request.Headers.Where(x => claimTypes.Contains(x.Key));
var user = Request.HttpContext.User;
if (claimHeads == null) return Ok();
var returnObj = new JObject();
foreach (var ch in claimHeads)
{
returnObj.Add(ch.Key, ch.Value.ToString());
}
return Ok(returnObj);
}
[HttpGet]
public IActionResult HeadClaims()
{
var claimTypes = new List<string> { "name", "phone", "userId", "introduce" };
var claimHeads = Request.Headers.Where(x => claimTypes.Contains(x.Key));
var user = Request.HttpContext.User;
if (claimHeads == null) return Ok();
var returnObj = new JObject();
foreach (var ch in claimHeads)
{
returnObj.Add(ch.Key, ch.Value.ToString());
}
return Ok(returnObj);
}
[Route("headclaims")]
[HttpGet, MapToApiVersion("2.0")]
public IActionResult HeadClaimsv2()
{
var claimTypes = new List<string> { "name", "phone" };
var claimHeads = Request.Headers.Where(x => claimTypes.Contains(x.Key));
var user = Request.HttpContext.User;
if (claimHeads == null) return Ok();
var returnObj = new JObject();
foreach (var ch in claimHeads)
{
returnObj.Add(ch.Key, ch.Value.ToString());
}
return Ok(returnObj);
}
[HttpGet, MapToApiVersion("2.0")]
public IActionResult HeadClaimsv2()
{
var claimTypes = new List<string> { "name", "phone" };
var claimHeads = Request.Headers.Where(x => claimTypes.Contains(x.Key));
var user = Request.HttpContext.User;
if (claimHeads == null) return Ok();
var returnObj = new JObject();
foreach (var ch in claimHeads)
{
returnObj.Add(ch.Key, ch.Value.ToString());
}
return Ok(returnObj);
}
}