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.csUserV2Controller.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
        }
    }
}

 

posted @ 2022-12-29 13:20  燕钰达  阅读(1273)  评论(0编辑  收藏  举报