当前位置: 首页 > 热点

武装你的WEBAPI-OData Versioning

2023-05-05 20:44:27 来源:博客园

本文属于OData系列


(相关资料图)

目录

武装你的WEBAPI-OData入门武装你的WEBAPI-OData便捷查询武装你的WEBAPI-OData分页查询武装你的WEBAPI-OData资源更新Delta武装你的WEBAPI-OData之EDM武装你的WEBAPI-OData常见问题武装你的WEBAPI-OData使用Endpoint武装你的WEBAPI-OData聚合查询武装你的WEBAPI-OData Versioning

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

介绍

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

版本管理

首先对现有的项目安装这个包:

Install-Package Asp.Versioning.OData

在Program.cs文件中修改一下:

var builder = WebApplication.CreateBuilder( args );builder.Services.AddControllers().AddOData();builder.Services.AddProblemDetails();builder.Services.AddApiVersioning().AddOData(    options =>    {        options.AddRouteComponents();    } );var app = builder.Build();app.MapControllers();app.Run();

然后在需要控制版本的控制器上加上[ApiVersion]修饰就可以了。

[ApiVersion( 1.0 )]public class PeopleController : ODataController{    [EnableQuery]    public IActionResult Get() => Ok( new[] { new Person() } );}

注意,默认的版本是1.0,不过最好显式声明一下。

EDM升级

EDM根据版本不同也会有一些区别,需要分别进行配置,原来的GetEdm()模式显得有点麻烦,而EDM配置在这个库中变得非常灵活,使用的是Configuration模式。

示例代码如下:

public class DeviceInfoModelConfiguration : IModelConfiguration{public void Apply(ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix){switch (apiVersion.MajorVersion){case 1:builder.EntitySet("DeviceInfoes").EntityType.HasKey(p => p.DeviceId);break;case 2:builder.EntitySet("DeviceInfos").EntityType.HasKey(p => p.DeviceId).Ignore(w => w.Layout);break;default:break;};}}

只需要实现IModelConfiguration接口,并在Apply函数中根据版本对实体或者DTO对象进行配置,不同版本的EDM可以不一样。

一般实践是一个实体对象一个IModelConfiguration,方便后面管理。

配置Swagger

因为有重复配置的模型,直接使用默认的Swagger会报错,这个时候需要使用到Versioned API Explorer,对Swagger拓展版本信息。

Install-Package Asp.Versioning.OData.ApiExplorer

安装Asp.Versioning.OData.ApiExplorer,重新改造一下Program.cs文件:

var builder = WebApplication.CreateBuilder( args );builder.Services.AddControllers().AddOData();builder.Services.AddProblemDetails();builder.Services.AddEndpointsApiExplorer();builder.Services.AddApiVersioning()                .AddOData( options => options.AddRouteComponents() )                .AddODataApiExplorer(                     // format the version as ""v"major[.minor][-status]"                     options => options.GroupNameFormat = ""v"VVV" );services.AddTransient, ConfigureSwaggerOptions>();services.AddSwaggerGen();var app = builder.Build();app.UseSwagger();app.UseSwaggerUI(    options =>    {        foreach ( var description in app.DescribeApiVersions() )        {            var url = $"/swagger/{description.GroupName}/swagger.json";            var name = description.GroupName.ToUpperInvariant();            options.SwaggerEndpoint( url, name );        }    } );app.MapControllers();app.Run();

还需要一个配置的类如下:

public class ConfigureSwaggerOptions : IConfigureOptions{  readonly IApiVersionDescriptionProvider provider;  public ConfigureSwaggerOptions( IApiVersionDescriptionProvider provider ) =>    this.provider = provider;  public void Configure( SwaggerGenOptions options )  {    foreach ( var description in provider.ApiVersionDescriptions )    {      options.SwaggerDoc(        description.GroupName,          new OpenApiInfo()          {            Title = $"Example API {description.ApiVersion}",            Version = description.ApiVersion.ToString(),          } );    }  }}

这样,swagger界面就可以下拉选择不同版本的API了。

旧系统升级

WEBAPI Versioning对这个内容有介绍,其中需要注意的是,基于路径的版本匹配并不支持默认版本的特性,对于以前系统直接使用api/开头的控制器,并不能直接默认转到到api/v1(参考介绍)。为了兼容旧系统,我们只能在ASP.NET CORE的管线上想想办法:插入一个中间件,对路径进行判断,如果是api开头的,就直接转到api/v1;如果是api/v开头的,那么就直接下一步。

public class RedirectMiddlewareForV1    {        private readonly RequestDelegate _next;        public RedirectMiddlewareForV1(RequestDelegate next)        {            _next = next;        }        public async Task InvokeAsync(HttpContext context)        {            if (context.Request.Path.StartsWithSegments("/api") && !context.Request.Path.Value.StartsWith("/api/v"))            {                context.Response.Redirect(context.Request.Path.Value.Replace("/api/", "/api/v1/"));            }            else            {                await _next(context);            }        }    }

然后在configure函数中注册这个中间件就可以了。

app.UseMiddleware();

请注意,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指定),这样程序在扫描的过程中会重复对对象进行生成。解决办法有两种:在EDM正确配置控制器中的Action或者Function,不要有不在OData路由的路径。(UseODataRouteDebug()可以启用$odata的路径,这样可以查看哪些路径不被OData解析)为每一个对象使用唯一名称:在配置中增加options.CustomSchemaIds(type => type.AssemblyQualifiedName)这个项目,所有的swagger对外名称会变成随机生成的值,也可以规避这个问题。详细分析看这个:Ignoring property on EDM causes InvalidOperationException: Can"t use schemaId "\(B" for type "\)A.B". The same schemaId is already used for type "$A.B" · Issue #772 · dotnet/aspnet-api-versioning (github.com)无法加载Swagger,提示System.MissingMethodException: Method not found: "Microsoft.OData.ModelBuilder.Config.DefaultQuerySettings Microsoft.AspNetCore.OData.ODataOptions.get_QuerySettings()这个是版本问题,本人使用的OData版本在8.1.0,有一些破坏性更改,只要保持引用的OData版本<= 8.0.12就可以了。详细分析看这个MissingMethodException with OData v8.1.0 · Issue #980 · dotnet/aspnet-api-versioning (github.com)找不到DescribeApiVersions()方法app找不到这个方法,大概率是在.NET 6的Minimal API之前的代码升级出现的,之前app是用IWebhostBuilder构建的,而现在的app是直接用过WebApplication构建得到的,含义不同,最简单的方法是改造一下,使用WebApplication重写一下Startup内容。参考API Documentation · dotnet/aspnet-api-versioning Wiki (github.com)API versioning extension with ASP.NET Core OData 8 - OData (microsoft.com)

关键词:

相关阅读

Copyright   2015-2022 人人技术网 版权所有  备案号:粤ICP备18023326号-36   联系邮箱:8557298@qq.com