武装你的WEBAPI-OData之API版本管理

WEBAPI,版本,API,管理 · 浏览次数 : 329

小编点评

**版本控制和管理的解决方案** **ASP.NET API Versioning** 是一个提供 ASP.NET WEBAPI 版本管理的包,支持 ASP.NET、ASP.NET CORE、ASP.NET CORE OData。 **版本控制** * ASP.NET API Versioning 提供版本控制功能,允许您记录和管理版本变更。 * 版本控制系统会记录版本变更的详细信息,包括旧版本、新版本和发布日期。 * 您可以在版本控制工具中查看版本历史,跟踪应用程序的版本变化。 **管理** * ASP.NET API Versioning 提供管理工具,允许您控制应用程序的版本。 * 您可以在版本控制工具中设置版本发布策略,例如强制更新或延迟发布。 * 您还可以创建版本标签,以表示应用程序的不同的版本。 **步骤** 1. **安装** ASP.NET API Versioning。 2. **配置**版本控制和管理工具。 3. **编写版本控制指令**,例如 `dotnet tool version add my-app`。 4. **发布应用程序**到版本控制系统中。 5. **查看版本历史**,跟踪应用程序的版本变化。 6. **设置版本发布策略**,例如 `dotnet tool version set-property Enabled true`。 **注意** * 版本控制和管理是可选的,您可以根据您的需求进行配置。 * 您需要在应用程序启动时加载版本控制工具。 * 版本控制工具通常与版本控制系统集成,例如 GitLab、GitHub 或 Azure DevOps。

正文

本文属于OData系列

Intro

对外提供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<DeviceInfo>("DeviceInfoes").EntityType.HasKey(p => p.DeviceId);
				break;
			case 2:
				builder.EntitySet<DeviceInfo>("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<IConfigureOptions<SwaggerGenOptions>, 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<SwaggerGenOptions>
{
  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"))
            {
                //千万小心,一定需要保留原来的QueryString
                var newUrl = $"{context.Request.Path.Value.Replace("/api/", "/api/v1/")}{context.Request.QueryString}";
                //permanent指示永久迁移,preserveMethod指示保留原来的谓词与body
                context.Response.Redirect(newUrl, permanent: true, preserveMethod: true);
            }
            else
            {
                await _next(context);
            }
        }


    }

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

app.UseMiddleware<RedirectMiddlewareForV1>();

请注意:

  • context.Request.Path.StartsWithSegments函数只能匹配完整的路径词汇,/api/v2去匹配/api/v会返回false。
  • 另外需要了解HTTP 301/302/307/308之间的区别,如果需要保留原来的请求body,需要使用307/308,308是永久移动。
  • Redirect并不保留原来的QueryString,需要手动拼接。

FAQ

  1. 无法正确显示不同版本的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指定),这样程序在扫描的过程中会重复对对象进行生成。解决办法有两种:
  1. 无法加载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)
  2. 找不到DescribeApiVersions()方法
    app找不到这个方法,大概率是在.NET 6的Minimal API之前的代码升级出现的,之前app是用IWebhostBuilder构建的,而现在的app是直接用过WebApplication构建得到的,含义不同,最简单的方法是改造一下,使用WebApplication重写一下Startup内容。

参考

与武装你的WEBAPI-OData之API版本管理相似的内容:

武装你的WEBAPI-OData之API版本管理

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

武装你的WEBAPI-OData聚合查询

本文属于OData系列 Introduction ODATA v4提出了新的聚合查询功能,这对于ODATA的基本查询能力($expand等)是一个非常大的补充。ODATA支持的聚合查询功能,可以对数据进行统计分析,例如求和、平均值、最大/最小值、分组等。 聚合查询是通过$apply关键字实现的。使用

武装你的WEBAPI-OData与DTO

本文属于OData系列文章 Intro 前面写了很多有关OData使用的文章,很多读者会有疑问,直接将实体对象暴露给最终用户会不会有风险?$expand在默认配置的情况下,数据会不会有泄露风险? 答案是肯定的,由于OData的特性,提供给我们便捷同时也会带来一些风险。很多地方推荐使用DTO模式来隔离

武装你的WEBAPI-OData使用Endpoint

本文属于 OData 系列文章 Introduction 更新: 由于新版的 OData 已经默认使用了 endpoint 模式(Microsoft.AspNetCore.OData 8.0.0),不再需要额外配置,本文已经过时(asp.net core 3.1)。 最近看 OData 的 devb

颜值经济下,车企的必备武器

摘要:对于买车的你,除了性价比,“颜价比”占多少呢? 新消费需求的冲击 时代赋予新时代年轻人丰富多彩的物质世界,他们受到互联网、即时通讯、智能手机等科技产物的影响很大,这个群体在消费方面既感性又理性,正在逐渐成为消费的主力军。 根据《2020年中国消费者调查报告》,二线及以下城市“年轻购物达人”仅占

DTT第7期直播回顾 | 低代码应用构建流程和适用场景,与你想的一样吗?

摘要:本期直播主题是《揭秘华为云低代码技术微认证》,向开发者们讲述低代码的发展历程,介绍华为低代码平台应用魔方AppCube的开发能力,解读华为低代码的认证和学习体系 本期直播详解 本期直播主题是《揭秘华为云低代码技术微认证》,华为云PaaS服务产品部资深专家董鑫武向开发者们讲述低代码的发展历程,介

[转帖]调优"四剑客"的实战演练,福尔摩斯•K带你轻松优化性能

前言 天下武功,唯快不破。在侦探的世界中,破案效率永远是衡量一名侦探能力的不二法门。作为推理界冉冉升起的新星,大侦探福尔摩斯·K凭借着冷静的头脑、严谨的思维,为我们展现了一场场华丽而热血的推理盛宴。 接下来,我们不仅仅是看客,还将追随福尔摩斯·K的脚步,体验一场身临其境的冒险。一起寻访产生数据库性能

前端远程办公系列 —— 整体介绍

整体介绍 前言 有些公司需要你有远程办公的经验 远程办公是某些公司和组织的一种工作方式。比如公司运作以天津为根据地,还有一些团队在武汉,忙不过来时还有外包 本系列就以前端工程师的视角,以一个真实的场景来说一下这个事情。会说遇到了什么问题、如何协作、用到什么新工具,什么技术... 背景 张三,一直从事

超快的 Python 包管理工具「GitHub 热点速览」

天下武功,无坚不破,唯快不破! 要想赢得程序员的欢心,工具的速度至关重要。仅需这一优势,即可使其在众多竞争对手中脱颖而出,迅速赢得开发者的偏爱。以这款号称下一代极速 Python 包管理工具——uv 为例,它的核心竞争力在于「快」和「丝滑替代」。自年初开源以来,不到半年便实现了 Star 数破万的壮

比赛获奖的武林秘籍:07 一文速通电子设计大赛,电子人必看的获奖秘籍

本文主要介绍了全国大学生电子设计大赛的含义、比赛形式、组队技巧,结合自身经历讲解了备赛指南,同时对往年题目进行了分析和总结。