C# .net core(.net 6) WebApi 版本控制(Versioning) 并配置Swagger
注:.net 6 注册使用Swagger 封装扩展静态类方法
注: C# .net core(.net 6) 注册使用Swagger 封装扩展静态类方法
一、Version版本控制
1、安装两个NuGet包
Microsoft.AspNetCore.Mvc.Versioning
Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
2、添加对ApiVersioning、AddVersionedApiExplorer的注册
1)、添加API版本控制支持
//添加API版本控制支持 builder.Services.AddApiVersioning(config => { //默认的API版本 config.DefaultApiVersion = new ApiVersion(1, 0); //未指定API版本时,设置默认版本 config.AssumeDefaultVersionWhenUnspecified = true; //是否在响应的Header信息中返回API版本信息 config.ReportApiVersions = true; });
2)、定义默认API版本和API支持的版本。
//配置API版本信息 builder.Services.AddVersionedApiExplorer(options => { //未指定API版本时,设置默认版本 options.AssumeDefaultVersionWhenUnspecified = true; //API版本分组名称 options.GroupNameFormat = "'v'VVV"; });
3、在Controllers中使用版本控制
注:使用URI路径版本控制方式实现版本控制(符合restfull路由风格)
1)、在不同版本Controller中添加版本控制 [ApiVersion("")] ,配置路由
User.cs是model类,UserController.cs、UserV2Controller.cs是文件不同名的同一接口(User接口)
i、User.cs
namespace Demo01 { /// <summary> /// 用户 /// </summary> public class User { /// <summary> /// 用户ID /// </summary> public int Id { get; set; } /// <summary> /// 用户姓名 /// </summary> public string? Name { get; set; } } }
ii、UserController.cs
using Demo01; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Demo02.Controllers { /// <summary> /// 用户管理 /// </summary> [ApiVersion("1.0")] [Route("[controller]/V{version:apiVersion}")] [ApiController] public class UserController : ControllerBase { private readonly ILogger<UserController> _logger; /// <summary> /// 构造函数 /// </summary> /// <param name="logger"></param> public UserController(ILogger<UserController> logger) { _logger = logger; } /// <summary> /// 获取用户信息 /// </summary> /// <returns></returns> [HttpGet()] public User GetUser() { return new() { Id = 1, Name = "admin" }; } /// <summary> /// 添加用户 /// </summary> /// <param name="user">用户信息</param> /// <returns></returns> [HttpPost()] public int AddUser(User user) { return 1; } /// <summary> /// 更新用户信息 /// </summary> /// <param name="user"></param> /// <returns></returns> [HttpPut()] public int UpdateUser(User user) { return 1; } /// <summary> /// 删除用户 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpDelete()] public int DeleteUser(int id) { return 1; } } }
iii、UserV2Controller.CS
using Demo01; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Demo02.Controllers.V2 { /// <summary> /// 用户管理 /// </summary> [ApiVersion("2.0")] [Route("[controller]/V{version:apiVersion}")] [ApiController] public class UserController : ControllerBase { private readonly ILogger<UserController> _logger; /// <summary> /// 构造函数 /// </summary> /// <param name="logger"></param> public UserController(ILogger<UserController> logger) { _logger = logger; } /// <summary> /// 获取用户信息 /// </summary> /// <returns></returns> [HttpGet()] public User GetUser() { return new() { Id = 2, Name = "admin2" }; } /// <summary> /// 添加用户 /// </summary> /// <param name="user">用户信息</param> /// <returns></returns> [HttpPost()] public int AddUser(User user) { return 2; } /// <summary> /// 更新用户信息 /// </summary> /// <param name="user"></param> /// <returns></returns> [HttpPut()] public int UpdateUser(User user) { return 1; } /// <summary> /// 删除用户 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpDelete()] public int DeleteUser(int id) { return 1; } } }
2)、接口不同版本访问方式:
V1: V2:
4、API版本控制策略(该实现采用URL路径版本控制)
参考张飞洪:NET 6当中的Web API版本控制
我们这里讨论三种最常用的API版本控制策略。
1)URI路径版本控制
URI路径策略很受欢迎,因为它更易于实现。一般我们会在URI路径的某个地方插入一个版本指示符,如v1或v2,如下所示:https://iot.com/api/v1/products
以上是版本1,如果要升级为版本2,我们直接将v1改成v2即可:https://iot.com/api/v2/products
注意在切换API版本时,为了获得正确的API返回的内容,原来的URI作为缓存键可能会失效。基于路径的版本控制很通用,几乎大部分的平台或者语言都支持这种方法,几乎成为了一种默认的标准,我们的案例代码默认也是采用这种策略。
2)Header版本控制
使用Header(头部)进行版本控制,头部一个谓词,并且有一个头部值,该值就是调用者需要分辨的版本号,如以下示例内容:GET /api/products HTTP/1.1 Host: localhost:5001 Content-Type: application/json x-api-version: 2
此策略有个好处是它不会损坏URI。但是,在客户端使用这些类型的API会比较麻烦一些。
3)查询字符串版本控制
查询字符串(Query string)根据API的使用者的需要,使用查询字符串指定API的版本。,如果请求中没有查询字符串,则应该具有API的隐式默认版本。我们看一个示例:https://iot.com/api/products?api-version=2
以上三种策略都有各自的使用场景,具体应该选择哪一个,取决于消费方法以及未来的规划。
二、Version版本控制下配置Swagger(封装成扩展静态类方法)
注:将swagger注册、实现封装成静态类方法详细配置见---C# .net core(.net 6) 注册使用Swagger 封装扩展静态类方法
1、注册Swagger
/// <summary> /// Swagger注册方法 /// </summary> /// <param name="builder"></param> public static void AddSwaggerGenExt(this WebApplicationBuilder builder) { //注册Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { #region 配置Swagger { var provider = builder.Services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); // 多版本控制 foreach (var description in provider.ApiVersionDescriptions) { // 添加文档信息 c.SwaggerDoc(description.GroupName, new OpenApiInfo { Title = "CoreWebApi", Version = description.ApiVersion.ToString(), Description = "ASP.NET CORE WebApi", Contact = new OpenApiContact { Name = "博客园燕钰达", Email = string.Empty, Url = new Uri("https://www.cnblogs.com/yyd-sun/") } }); } //在Swagger文档显示的API地址中将版本信息参数替换为实际的版本号 c.DocInclusionPredicate((Version, ApiDescription) => { if (!Version.Equals(ApiDescription.GroupName)) return false; IEnumerable<string>? values = ApiDescription!.RelativePath .Split('/') .Select(v => v.Replace("V{version}", ApiDescription.GroupName));//"V{version}": 与Controller中的路由版本配置保持一致 ApiDescription.RelativePath = String.Join("/", values); return true; }); //取消API文档需要输入版本信息 c.OperationFilter<RemoveVersionParameter>(); } #endregion #region 配置展示注释 { var path = Path.Combine(AppContext.BaseDirectory, "Demo02.xml"); // xml文档绝对路径 c.IncludeXmlComments(path, true); // true : 显示控制器层注释 c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序,如果有多个,就可以看见效果了。 } #endregion }); }
2、Swagger使用方法
/// <summary> /// Swagger使用方法 /// </summary> /// <param name="app"></param> public static void UseSwaggerExt(this WebApplication app) { #region 使用Swagger //使用Swagger app.UseSwagger(); // 配置SwaggerUI app.UseSwaggerUI(c => { var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>(); foreach (var description in provider.ApiVersionDescriptions) { //c.SwaggerEndpoint("/swagger/v1/swagger.json", "CoreAPI"); 单版本 c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", "CoreAPI" + description.ApiVersion); } //c.RoutePrefix = string.Empty; }); #endregion } }
3、SwaggerExtension.cs
using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.OpenApi.Models; namespace Demo02.Utility.Swagger { /// <summary> /// Swagger /// </summary> public static class SwaggerExtension { /// <summary> /// Swagger注册方法 /// </summary> /// <param name="builder"></param> public static void AddSwaggerGenExt(this WebApplicationBuilder builder) { //注册Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { #region 配置Swagger { var provider = builder.Services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); // 多版本控制 foreach (var description in provider.ApiVersionDescriptions) { // 添加文档信息 c.SwaggerDoc(description.GroupName, new OpenApiInfo { Title = "CoreWebApi", Version = description.ApiVersion.ToString(), Description = "ASP.NET CORE WebApi", Contact = new OpenApiContact { Name = "博客园燕钰达", Email = string.Empty, Url = new Uri("https://www.cnblogs.com/yyd-sun/") } }); } //在Swagger文档显示的API地址中将版本信息参数替换为实际的版本号 c.DocInclusionPredicate((Version, ApiDescription) => { if (!Version.Equals(ApiDescription.GroupName)) return false; IEnumerable<string>? values = ApiDescription!.RelativePath .Split('/') .Select(v => v.Replace("V{version}", ApiDescription.GroupName));//"V{version}": 与Controller中的路由版本配置保持一致 ApiDescription.RelativePath = String.Join("/", values); return true; }); //取消API文档需要输入版本信息 c.OperationFilter<RemoveVersionParameter>(); } #endregion #region 配置展示注释 { var path = Path.Combine(AppContext.BaseDirectory, "Demo02.xml"); // xml文档绝对路径 c.IncludeXmlComments(path, true); // true : 显示控制器层注释 c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序,如果有多个,就可以看见效果了。 } #endregion }); } /// <summary> /// Swagger使用方法 /// </summary> /// <param name="app"></param> public static void UseSwaggerExt(this WebApplication app) { #region 使用Swagger //使用Swagger app.UseSwagger(); // 配置SwaggerUI app.UseSwaggerUI(c => { var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>(); foreach (var description in provider.ApiVersionDescriptions) { //c.SwaggerEndpoint("/swagger/v1/swagger.json", "CoreAPI"); 单版本 c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", "CoreAPI" + description.ApiVersion); } // c.RoutePrefix = string.Empty; }); #endregion } } }