翼度科技»论坛 编程开发 .net 查看内容

武装你的WEBAPI-OData Versioning

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
本文属于OData系列
目录

对外提供WEBAPI时,如果遇上了版本升级,那么控制WEBAPI的版本也是非常必要的。OData官方提供了版本控制以及管理的解决方案,我个人是实践体会是不好用,好在社区提供了对应的nuget包,与.NET主版本同步更新。
介绍

ASP.NET API Versioning是一个提供ASP.NET WEBAPI版本管理的包,支持ASP.NET、ASP.NET CORE、ASP.NET CORE ODATA,作者以前是微软的员工,现在不在微软工作了,因此原先的命名空间不能继续用了。现在这个项目已经加入.NET Foundation,作者也非常活跃。
版本管理

首先对现有的项目安装这个包:
  1. Install-Package Asp.Versioning.OData
复制代码
在Program.cs文件中修改一下:
  1. var builder = WebApplication.CreateBuilder( args );
  2. builder.Services.AddControllers().AddOData();
  3. builder.Services.AddProblemDetails();
  4. builder.Services.AddApiVersioning().AddOData(
  5.     options =>
  6.     {
  7.         options.AddRouteComponents();
  8.     } );
  9. var app = builder.Build();
  10. app.MapControllers();
  11. app.Run();
复制代码
然后在需要控制版本的控制器上加上[ApiVersion]修饰就可以了。
  1. [ApiVersion( 1.0 )]
  2. public class PeopleController : ODataController
  3. {
  4.     [EnableQuery]
  5.     public IActionResult Get() => Ok( new[] { new Person() } );
  6. }
复制代码
注意,默认的版本是1.0,不过最好显式声明一下。
EDM升级

EDM根据版本不同也会有一些区别,需要分别进行配置,原来的GetEdm()模式显得有点麻烦,而EDM配置在这个库中变得非常灵活,使用的是Configuration模式。
示例代码如下:
  1. public class DeviceInfoModelConfiguration : IModelConfiguration
  2. {
  3.         public void Apply(ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix)
  4.         {
  5.                 switch (apiVersion.MajorVersion)
  6.                 {
  7.                         case 1:
  8.                                 builder.EntitySet<DeviceInfo>("DeviceInfoes").EntityType.HasKey(p => p.DeviceId);
  9.                                 break;
  10.                         case 2:
  11.                                 builder.EntitySet<DeviceInfo>("DeviceInfos").EntityType.HasKey(p => p.DeviceId).Ignore(w => w.Layout);
  12.                                 break;
  13.                         default:
  14.                                 break;
  15.                 };
  16.         }
  17. }
复制代码
只需要实现IModelConfiguration接口,并在Apply函数中根据版本对实体或者DTO对象进行配置,不同版本的EDM可以不一样。
一般实践是一个实体对象一个IModelConfiguration,方便后面管理。
配置Swagger

因为有重复配置的模型,直接使用默认的Swagger会报错,这个时候需要使用到Versioned API Explorer,对Swagger拓展版本信息。
  1. Install-Package Asp.Versioning.OData.ApiExplorer
复制代码
安装Asp.Versioning.OData.ApiExplorer,重新改造一下Program.cs文件:
  1. var builder = WebApplication.CreateBuilder( args );
  2. builder.Services.AddControllers().AddOData();
  3. builder.Services.AddProblemDetails();
  4. builder.Services.AddEndpointsApiExplorer();
  5. builder.Services.AddApiVersioning()
  6.                 .AddOData( options => options.AddRouteComponents() )
  7.                 .AddODataApiExplorer(
  8.                      // format the version as "'v'major[.minor][-status]"
  9.                      options => options.GroupNameFormat = "'v'VVV" );
  10. services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
  11. services.AddSwaggerGen();
  12. var app = builder.Build();
  13. app.UseSwagger();
  14. app.UseSwaggerUI(
  15.     options =>
  16.     {
  17.         foreach ( var description in app.DescribeApiVersions() )
  18.         {
  19.             var url = $"/swagger/{description.GroupName}/swagger.json";
  20.             var name = description.GroupName.ToUpperInvariant();
  21.             options.SwaggerEndpoint( url, name );
  22.         }
  23.     } );
  24. app.MapControllers();
  25. app.Run();
复制代码
还需要一个配置的类如下:
  1. public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
  2. {
  3.   readonly IApiVersionDescriptionProvider provider;
  4.   public ConfigureSwaggerOptions( IApiVersionDescriptionProvider provider ) =>
  5.     this.provider = provider;
  6.   public void Configure( SwaggerGenOptions options )
  7.   {
  8.     foreach ( var description in provider.ApiVersionDescriptions )
  9.     {
  10.       options.SwaggerDoc(
  11.         description.GroupName,
  12.           new OpenApiInfo()
  13.           {
  14.             Title = $"Example API {description.ApiVersion}",
  15.             Version = description.ApiVersion.ToString(),
  16.           } );
  17.     }
  18.   }
  19. }
复制代码
这样,swagger界面就可以下拉选择不同版本的API了。

旧系统升级

WEBAPI Versioning对这个内容有介绍,其中需要注意的是,基于路径的版本匹配并不支持默认版本的特性,对于以前系统直接使用api/开头的控制器,并不能直接默认转到到api/v1(参考介绍)。为了兼容旧系统,我们只能在ASP.NET CORE的管线上想想办法:插入一个中间件,对路径进行判断,如果是api开头的,就直接转到api/v1;如果是api/v开头的,那么就直接下一步。
  1.     public class RedirectMiddlewareForV1
  2.     {
  3.         private readonly RequestDelegate _next;
  4.         public RedirectMiddlewareForV1(RequestDelegate next)
  5.         {
  6.             _next = next;
  7.         }
  8.         public async Task InvokeAsync(HttpContext context)
  9.         {
  10.             if (context.Request.Path.StartsWithSegments("/api") && !context.Request.Path.Value.StartsWith("/api/v"))
  11.             {
  12.                 context.Response.Redirect(context.Request.Path.Value.Replace("/api/", "/api/v1/"));
  13.             }
  14.             else
  15.             {
  16.                 await _next(context);
  17.             }
  18.         }
  19.     }
复制代码
然后在configure函数中注册这个中间件就可以了。
  1. app.UseMiddleware<RedirectMiddlewareForV1>();
复制代码
请注意,context.Request.Path.StartsWithSegments函数只能匹配完整的路径词汇,/api/v2去匹配/api/v会返回false。
FAQ


  • 无法正确显示不同版本的Swagger,提示InvalidOperationException: Can't use schemaId "\(B" for type "\)A.B". The same schemaId is already used for type "$A.B"
    这个问题是由多次对同一个类型Schema生成造成的。最常见的情况是你的控制器有方法不属于OData Routing的一部分(比如直接使用HttpGet指定),这样程序在扫描的过程中会重复对对象进行生成。解决办法有两种:

<ol start="2">无法加载Swagger,提示System.MissingMethodException: Method not found: 'Microsoft.OData.ModelBuilder.Config.DefaultQuerySettings Microsoft.AspNetCore.OData.ODataOptions.get_QuerySettings()
这个是版本问题,本人使用的OData版本在8.1.0,有一些破坏性更改,只要保持引用的OData版本

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具