1、背景
在实际环境中,常常需要对API进行版本的管理,以免方法修改后造成不可控。网上在介绍.NET环境中常用版本控制的是Microsoft.AspNetCore.Mvc.Versioning
包,但改包已经弃用了,因此这次使用它的升级包(还是原作者开发的)Asp.Versioning.Mvc
。
这是原来的包:
这是新的包:Asp.Versioning.Mvc
2、具体实现
2.1、program.cs中的配置
//版本控制
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new Asp.Versioning.ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
// 结合多种版本控制方式
options.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader("version"),
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-API-Version"),
new MediaTypeApiVersionReader("version")
);
}).AddMvc();
有四种方法的版本控制,都给加上。
new QueryStringApiVersionReader("version"),
是指通过URL中的参数来确认。例如:http://xxxx/getuser?version=1
。
new UrlSegmentApiVersionReader()
是指通过路由进行确认,例如:http://xxxx/api/v1/getuser
和http://xxxx/api/v2/getuser
。
new HeaderApiVersionReader("X-API-Version")
是指通过header
头信息来确认。例如,设置header.add("X-API-Version","1")
来设置
new MediaTypeApiVersionReader("version")
是通过content_type
信息来设置。
其中通过路由的设置优先级最高(就是new UrlSegmentApiVersionReader()
的优先级最高)。
在这些配置之前,就是设置一些默认值。例如默认的版本是1.0(options.DefaultApiVersion = new Asp.Versioning.ApiVersion(1, 0);
)等,运行后就可以通过Swagger
进行查看了。
另外,将配置以下AddSwaggerGen
,可以查询不同版本的API
接口
builder.Services.AddSwaggerGen(options =>
{
// 为不同版本定义Swagger文档
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "MyAPIv1",
Description = "API 文档: 版本 1"
});
options.SwaggerDoc("v2", new OpenApiInfo
{
Version = "v2",
Title = "MyAPIv2",
Description = "API 文档: 版本 2"
});
// 使用解决冲突的策略
options.ResolveConflictingActions(apiDescriptions =>
{
return apiDescriptions.First();
});
});
以及为每个版本设置不同的文档
app.UseSwaggerUI(options =>
{
// 为每个版本定义端点
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyAPIv1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "MyAPIv2");
});
因此完整的program.cs文件配置如下:
using Asp.Versioning;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
// 为不同版本定义Swagger文档
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "MyAPIv1",
Description = "API 文档: 版本 1"
});
options.SwaggerDoc("v2", new OpenApiInfo
{
Version = "v2",
Title = "MyAPIv2",
Description = "API 文档: 版本 2"
});
// 使用解决冲突的策略
options.ResolveConflictingActions(apiDescriptions =>
{
return apiDescriptions.First();
});
//添加注释
var file = Path.Combine(AppContext.BaseDirectory, "SwaggerDoc.xml"); // xml文档绝对路径
var path = Path.Combine(AppContext.BaseDirectory, file); // xml文档绝对路径
options.IncludeXmlComments(path, true); // true : 显示控制器层注释
options.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序,如果有多个,就可以看见效果了
});
//版本控制
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new Asp.Versioning.ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
// 结合多种版本控制方式
options.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader("version"),
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-API-Version"),
new MediaTypeApiVersionReader("version")
);
}).AddMvc();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
// 为每个版本定义端点
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyAPIv1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "MyAPIv2");
});
}
app.UseHttpsRedirection();
//app.UseStaticFiles();
app.UseAuthorization();
app.MapControllers();
app.Run();
2.2、Controller的设置
为每个controller
创建一个不同版本的文件夹,以及相同名称的controller
,以及内部相同的方法,如下图所示
这样,因为空间不同,可以创建两个相同名称的controller
了
具体代码如下:
namespace APIVersionDemo.Controllers.V2
{
[ApiVersion(1.0)]
[ApiController]
[Route("api/v{version:apiVersion}/[controller]/[action]")] // URI版本控制
// 注意:查询字符串、Header和媒体类型版本控制通常不需要额外的路由属性
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"1111", "111", "111", "111", "111", "111", "111", "111", "111", "111"
};
private readonly ILogger<WeatherForecastController> _logger;
/// <summary>
/// 123123
/// </summary>
/// <param name="logger"></param>
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
[MapToApiVersion(1.0)]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
这里面有如下注意事项:1、在action
上面的[MapToApiVersion(1.0)]
,其版本号可以与controller中的不同,甚至是更高也OK。例如。controller上标注了[ApiVersion(1.0)]
,但方法上面标注了[MapToApiVersion(3.0)]
。这个也是可以的。另外一个controller与其代码相同,只是1.0变成了2.0。
2.3 运行效果
2.3.1 uri访问
直接访问:http://localhost:5021/api/v1/WeatherForecast/Get
,没有任何问题。
2.3.2 通过参数访问
刚才提到了,uri
方式的优先级是最高的(就是在controller上面加上的那句[Route("api/v{version:apiVersion}/[controller]/[action]")]
),若想使用其他方式,则必须把controller改为正常的[Route("api/[controller]/[action]")]
,否则就会报如下的错误
因此,将controller中的route改为正常的。使用参数访问如下:
2.3.3、使用header访问
在header中添加key-value。其中X-API-Version
就是program中自定义设置的
2.3.4、使用MediaType访问
3、代码
代码下载地址
4、参考
主要参考了以下资料:
1、.NET 8 中 API 版本控制的最佳实践
2、ASP.NET Core WebAPI 版本控制