前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >ABP 适用性改造 - 添加 API 版本化支持

ABP 适用性改造 - 添加 API 版本化支持

作者头像
程序员宇说
发布于 2021-04-15 08:42:52
发布于 2021-04-15 08:42:52
1.1K00
代码可运行
举报
文章被收录于专栏:程序员宇说程序员宇说
运行总次数:0
代码可运行

Overview

在前面的文章里有针对 abp 的项目模板进行简化,构建了一个精简的项目模板,在使用过程中,因为我们暴露的 api 需要包含版本信息,我们采取的方式是将 api 的版本号包含在资源的 URI 中。因为 abp 默认的 api 是没有版本的概念的,所以这里为了实现 api 版本化需要针对 abp 项目的 api 路由进行改造,从而满足我们的需求。本篇文章则是实现这一改造过程的演示说明,希望可以对你有所帮助

完整的项目模板如下所示

模板源码地址:https://github.com/danvic712/ingos-abp-api-template

Step by Step

在 abp 项目中,可以通过如下的两种方式实现 api 接口的定义

  1. 传统的 web api 实现方式,通过定义 controller 来完成资源 api 构建
  2. 通过 abp 框架内置的 Auto API Controller 功能,将项目中定义的应用服务(application service),自动暴露成 api 接口

因为这里的两种方式在项目开发中我们都会使用到,所以这里需要针对这两种不同的方式都实现 api 版本化的支持

对于第一种方式的 api 版本化支持,我在之前的文章中有提到过,如果你有需要的话,可以点击此处进行查阅,这里就不再赘述了,本篇文章主要关注点在如何对 abp 自动生成的 api 接口进行改造,实现将 api 版本信息添加到路由中

因为这里我使用的是精简后的 abp 模板,与默认的 abp 项目中的程序集名称存在差异,程序集之间的对应关系如下所示,你可以对照默认的项目进行修改

  • xxx.API => xxx.HttpApi.Host
  • xxx.Application => xxx.Application

2.1、添加程序集

对于 api 版本化的实现,这里也是基于下面的两个类库来的,因此,在使用之前我们需要先在项目中通过 nuget 添加对于这两个程序集的引用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
## 添加 API 多版本支持
Install-Package Microsoft.AspNetCore.Mvc.Versioning

## 添加 Swagger 文档的 API 版本显示支持
Install-Package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

因为在 xxx.API 这个项目中已经使用到的 abp 的程序集中已经间接引用了 *.Versioning 这个程序集,所以这里就可以选择不添加,只需要将 *.Versioning.ApiExplorer 添加引用到项目即可

对于 xxx.Application 这个类库,因为不会关联到 Swagger 的相关设置,所以这里只需要在项目中添加 *.Versioning 的引用

2.2、路由改造

当所需的程序集引用添加完成之后,就可以针对 abp 生成的路由格式进行改造,从而实现我们想要添加 api 版本信息到路由地址中的目的

对于通过创建 controller 来暴露 api 服务的接口,我们可以直接在 controller or action 上添加 ApiVersion 特性,然后修改特性路由即可,示例代码如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class VaulesController : ControllerBase
{
	// action ...
}

而对于 abp 基于 application service 自动生成的 api,在默认的项目模板中,你可以在 *HttpApiHostModule 类中找到如下的配置,最终可以生成下图中的 api 路由格式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public override void ConfigureServices(ServiceConfigurationContext context)
{
	var configuration = context.Services.GetConfiguration();
	var hostingEnvironment = context.Services.GetHostingEnvironment();
	
	ConfigureConventionalControllers(context);
}

private void ConfigureConventionalControllers()
{
	Configure<AbpAspNetCoreMvcOptions>(options =>
	{
		options.ConventionalControllers.Create(typeof(XXXApplicationModule).Assembly);
	});
}

从 abp 的文档中可知,基于约定俗成的定义,所有根据 application service 自动生成的 api 全部会以 /api 开头,而路由路径中的的 */app/* 我们则可以通过修改 RootPath 变量值的方式进行调整,例如,你可以将 app 修改成 your-api-path-define

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void ConfigureConventionalControllers()
{
	Configure<AbpAspNetCoreMvcOptions>(options =>
	{
		options.ConventionalControllers.Create(typeof(XXXApplicationModule).Assembly, opts =>
            {
                opts.RootPath = "your-api-path-define";
            });
	});
}

这里调整之后的 api 路由就会变成 /api/your-api-path-define/*,因此这里我们就可以通过修改变量值的方式来实现路由中包含 api 的版本信息,eg. /api/v1/*

找到能够调整的地方后,我们就需要思考具体的改造方式了,如果这里我们写死变量值为 v1 or v2 的话,意味着整个 XXXApplicationModule 程序集中的 application service 生成的 api 版本就限制死了,后续的可扩展性就太差了,所以这里需要实现一个动态的配置

因此这里同样是借助了上面引用的组件包,选择通过添加 ApiVersion 特性的方式来标明应用服务所映射的 api 版本信息,例如下面对应生成的 api 版本为 1.0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ApiVersion("1.0")]
public class BookAppService :
	CrudAppService<
		Book, // The Book entity
		BookDto, // Used to show books
		Guid, // Primary key of the book entity
		PagedAndSortedResultRequestDto, // Used for paging/sorting
		CreateUpdateBookDto>, // Used to create/update a book
	IBookAppService // implement the IBookAppService
{
	public BookAppService(IRepository<Book, Guid> repository)
		: base(repository)
	{

	}
}

定义了服务对应的 api 版本之后,这里就可以通过路由模板变量值的方式来替换 RootPath 参数值,因为这里的路由相对于原来的方式来说是一种不确定的,所以这里我们将配置路由的方法放在 abp 的 PreConfigureServices 生命周期函数中,位于该函数中的代码会在整个项目所有模块的 ConfigureServices 方法执行之前执行,调整后的代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public override void PreConfigureServices(ServiceConfigurationContext context)
{
	PreConfigure<AbpAspNetCoreMvcOptions>(options =>
	{
		// 依据 api 版本信息动态设置路由信息
		options.ConventionalControllers.Create(typeof(IngosAbpTemplateApplicationModule).Assembly,
			opts => { opts.RootPath = "v{version:apiVersion}"; });
	});
}

public override void ConfigureServices(ServiceConfigurationContext context)
{
	var configuration = context.Services.GetConfiguration();
	var hostingEnvironment = context.Services.GetHostingEnvironment();
    
    ConfigureConventionalControllers(context);
}

private void ConfigureConventionalControllers(ServiceConfigurationContext context)
{
    // 基于 PreConfigureServices 中的配置进行
	Configure<AbpAspNetCoreMvcOptions>(options => { context.Services.ExecutePreConfiguredActions(options); });
}

当然,这里只是针对我们自己编写的应用服务进行的版本设定,对于 abp 框架所包含的一些 api 接口,可以直接在 PreConfigureServices 函数中通过直接指定 api 版本的方式来实现,例如这里我将权限相关的 api 接口版本设置为 1.0

PS,这里针对框架内置 api 的版本设定,并不会改变接口的路由地址,仅仅是为了下面将要实现的 swagger 依据 api 版本号进行分组显示时可以将内置的 api 暴露出来

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public override void PreConfigureServices(ServiceConfigurationContext context)
{
	PreConfigure<AbpAspNetCoreMvcOptions>(options =>
	{
		// 依据 api 版本信息动态设置路由信息
		options.ConventionalControllers.Create(typeof(IngosAbpTemplateApplicationModule).Assembly,
			opts => { opts.RootPath = "v{version:apiVersion}"; });

		// 指定内置权限相关 api 版本为 1.0
		options.ConventionalControllers.Create(typeof(AbpPermissionManagementHttpApiModule).Assembly,
			opts => { opts.ApiVersions.Add(new ApiVersion(1, 0)); });
	});
}

配置好路由之后,就可以将 api 版本服务以及给到 swagger 使用的 api explorer 服务注入到 IServiceCollection 中,这里的配置项和之前的方式一样就不做解释了,完善后的方法代码如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void ConfigureConventionalControllers(ServiceConfigurationContext context)
{
	Configure<AbpAspNetCoreMvcOptions>(options => { context.Services.ExecutePreConfiguredActions(options); });

	context.Services.AddAbpApiVersioning(options =>
	{
		options.ReportApiVersions = true;

		options.AssumeDefaultVersionWhenUnspecified = true;

		options.DefaultApiVersion = new ApiVersion(1, 0);

		options.ApiVersionReader = new UrlSegmentApiVersionReader();

		var mvcOptions = context.Services.ExecutePreConfiguredActions<AbpAspNetCoreMvcOptions>();
		options.ConfigureAbp(mvcOptions);
	});

	context.Services.AddVersionedApiExplorer(option =>
	{
		option.GroupNameFormat = "'v'VVV";

		option.AssumeDefaultVersionWhenUnspecified = true;
	});
}

2.3、Swagger 改造

因为改造前的项目是不存在 api 版本的概念的,所以默认的 swagger 是会显示出所有的接口,而当项目可以支持 api 版本化之后,这里就应该基于 api 版本生成不同的 json 文件,达到 swagger 可以基于 api 的版本来分组显示的目的

因为在上面的代码中已经将 api explorer 服务注入到了 IServiceCollection 中,所以这里可以直接使用 IApiVersionDescriptionProvider 获取到 api 的版本信息,从而据此生成不同的 swagger json 文件,swagger 相关的配置代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public override void ConfigureServices(ServiceConfigurationContext context)
{
	var configuration = context.Services.GetConfiguration();
	var hostingEnvironment = context.Services.GetHostingEnvironment();
    
    ConfigureSwaggerServices(context);
}

public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
	var app = context.GetApplicationBuilder();

	app.UseSwagger();
	app.UseAbpSwaggerUI(options =>
	{
		options.DocumentTitle = "IngosAbpTemplate API";

		// 默认显示最新版本的 api 
		//
		var provider = context.ServiceProvider.GetRequiredService<IApiVersionDescriptionProvider>();
		var apiVersionList = provider.ApiVersionDescriptions
			.Select(i => $"v{i.ApiVersion.MajorVersion}")
			.Distinct().Reverse();
		foreach (var apiVersion in apiVersionList)
			options.SwaggerEndpoint($"/swagger/{apiVersion}/swagger.json",
				$"IngosAbpTemplate API {apiVersion?.ToUpperInvariant()}");
	});
}

private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
{
	context.Services.AddAbpSwaggerGenWithOAuth(
		configuration["AuthServer:Authority"],
		options =>
		{
			// 获取 api 版本信息
			var provider = context.Services.BuildServiceProvider()
				.GetRequiredService<IApiVersionDescriptionProvider>();

			// 基于大版本生成 swagger 
			foreach (var description in provider.ApiVersionDescriptions)
				options.SwaggerDoc(description.GroupName, new OpenApiInfo
				{
					Contact = new OpenApiContact
					{
						Name = "Danvic Wang",
						Email = "danvic.wang@outlook.com",
						Url = new Uri("https://yuiter.com")
					},
					Description = "IngosAbpTemplate API",
					Title = "IngosAbpTemplate API",
					Version = $"v{description.ApiVersion.MajorVersion}"
				});

			options.DocInclusionPredicate((docName, description) =>
			{
				// 获取主要版本,如果不是该版本的 api 就不显示
				var apiVersion = $"v{description.GetApiVersion().MajorVersion}";

				if (!docName.Equals(apiVersion))
					return false;

				// 替换路由参数
				var values = description.RelativePath
					.Split('/')
					.Select(v => v.Replace("v{version}", apiVersion));

				description.RelativePath = string.Join("/", values);

				return true;
			});

			// 取消 API 文档需要输入版本信息
			options.OperationFilter<RemoveVersionFromParameter>();
		});
}

自此,整个关于 api 版本化的调整就已经完成了,完整的代码可以点击此处跳转到 github 上进行查看,最终实现效果如下所示

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-04-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
ABP微服务系列学习-搭建自己的微服务结构(三)
认证服务的话,ABP CLI生成的所有模板都包括了一个AuthServer。我们直接生成模板然后微调一下就可以直接用了。
饭勺oO
2023/10/18
7230
4. abp中的asp.net core模块剖析
abp通过这三个模块加载并配置了 asp.net core。,最主要的就是AbpAspNetCoreMvcModule模块类,abp如何基于aspnet core构建自己的控制器和AppServices,就是在这个类中。
Ryan_OVO
2023/10/19
2970
ABP微服务系列学习-搭建自己的微服务结构(一)
在原本的结构里面,由于默认服务引用的都是ABP原生的模块,所以结构目录里面没有包含modules目录,这里我们添加一个modules目录,用于存放我们的自定义模块。 在shared里面,我们再抽一个EventData的模块,用于消息队列共用数据实体。修改后结构如下图所示:
饭勺oO
2023/10/18
8680
ABP微服务系列学习-搭建自己的微服务结构(一)
浅入ABP(1):搭建基础结构的 ABP 解决方案
源码地址:https://github.com/whuanle/AbpBaseStruct
痴者工良
2021/04/26
8430
Abp源码分析之Abp最小系统
我们会发现,系统加载所有继承AbpModule的文件,并按序运行里面的方法实现对模块的配置
吴晓阳
2024/11/02
940
Abp源码分析之Abp最小系统
5. abp集成asp.net core
参照前篇《4. abp中的asp.net core模块剖析》,首先放张图,这也是asp.net core框架上MVC模块的扩展点
Ryan_OVO
2023/10/19
4640
5. abp集成asp.net core
浅入ABP(2):添加基础集成服务
上一篇,我们已经搭建起了一个基本的程序结构,下面我们来添加一些必要的服务,例如异常拦截器、跨域等。
痴者工良
2021/04/26
1.9K0
ASP.NET Core 实战:构建带有版本控制的 API 接口
  在上一篇的文章中,主要是搭建了我们的开发环境,同时创建了我们的项目模板框架。在整个前后端分离的项目中,后端的 API 接口至关重要,它是前端与后端之间进行沟通的媒介,如何构建一个 “好用” 的 API 接口,是需要我们后端人员好好思考的。   在系统迭代的整个过程中,不可避免的会添加新的资源,或是修改现有的资源,后端接口作为暴露给外界的服务,变动的越小,对服务的使用方造成的印象就越小,因此,如何对我们的 API 接口进行合适的版本控制,我们势必需要首先考虑。
程序员宇说
2019/09/11
1.3K0
ASP.NET Core 实战:构建带有版本控制的 API 接口
ABP 适用性改造 - 精简 ABP CLI 生成的项目结构
不管是公司或者个人都会有不同的开发习惯,通过建立项目模板,既可以使开发人员聚焦于业务功能的开发,也可以在一定程度上统一不同开发人员之间的开发风格。在使用 ABP 框架的过程中,对于 ABP 生成的默认项目模板,类库多,附加功能多,是目前在部门内部推行 ABP 过程中遇到的问题。因此,本篇文章将针对 ABP 默认生成的模板项目进行精简,构建出一个简化版的 ABP 项目模板
程序员宇说
2021/03/18
1.9K0
或许是你应该了解的一些 ASP.NET Core Web API 使用小技巧
  在目前的软件开发的潮流中,不管是前后端分离还是服务化改造,后端更多的是通过构建 API 接口服务从而为 web、app、desktop 等各种客户端提供业务支持,如何构建一个符合规范、容易理解的 API 接口是我们后端开发人员需要考虑的。在本篇文章中,我将列举一些我在使用 ASP.NET Core Web API 构建接口服务时使用到的一些小技巧,因才疏学浅,可能会存在不对的地方,欢迎指出。
程序员宇说
2019/07/31
1.5K0
(转载非原创)Abp太重了?轻量化Abp框架
文章来源:https://blog.zhangchi.fun/posts/lightweightabp/
xlj
2021/07/23
1.3K2
(转载非原创)Abp太重了?轻量化Abp框架
Asp.Net WebApi在swagger中添加版本控制
在Asp.Net WebApi中添加版本控制,同时在swagger中按版本显示接口
用户6362579
2020/06/11
2.3K0
.NET WebAPI 实现 接口版本控制并打通 Swagger支持
我们在开发 webapi 项目时如果遇到 api 接口需要同时支持多个版本的时候,比如接口修改了入参之后但是又希望支持老版本的前端(这里的前端可能是网页,可能是app,小程序 等等)进行调用,这种情况常见于 app,毕竟网页前端我们可以主动控制发布,只要统一发布后所有人的浏览器下一次访问网页时都会重新加载到最新版的代码,但是像 app 则无法保证用户一定会第一时间升级更新最新版的app,所以往往需要 api接口能够同时保持多个版本的逻辑,同支持新老版本的调用端app进行调用。
乌拉栋
2022/10/28
1.1K0
.NET WebAPI 实现 接口版本控制并打通 Swagger支持
Swashbuckle.AspNetCore3.0的二次封装与使用
之前写过一篇Swashbuckle.AspNetCore-v1.10 的使用,现在 Swashbuckle.AspNetCore 已经升级到 3.0 了,正好开新坑(博客重构)重新封装了下,将所有相关的一些东西抽取到单独的类库中,尽可能的避免和项目耦合,使其能够在其他项目也能够快速使用。
易墨
2018/10/10
9510
ABP微服务系列学习-搭建自己的微服务结构(二)
在解决方案根目录添加common.props,这个文件的作用是可以配置项目文件全局的一些属性,如忽略警告,全局PackageReference,语言版本等。
饭勺oO
2023/10/18
6320
ABP微服务系列学习-搭建自己的微服务结构(二)
动态We API(ABP官方文档翻译)
这个文档是针对ASP.NET Web API的。如果你对ASP.NET Core感兴趣,请参见ASP.NET Core文档。
yaphetsfang
2020/07/30
2.9K0
ASP.NET Core 3.x API版本控制
一般来说需要更改我们API的时候才考虑版本控制,但是我觉得我们不应该等到那时候来实现它,我们应该有一个版本策略从我们应用程序开发时就开始制定好我们的策略,我们一直遵循着这个策略进行开发。
HueiFeng
2020/06/03
7770
Abp vnext构建API接口服务
然后我们只需要修改一下其他的配置即可运行应用程序,开发人员在这个架构的基础上就可以愉快的撸代码了。 然而,ABP的学习才刚刚开始。ABP放弃了原有MVC的架构,使用了模块化架构,支持微服务,根据DDD模式和原则设计和开发,为应用程序提供分层模型。对于没有DDD、微服务开发经验的程序员来说,学习ABP难度比较大。
有态度的马甲
2020/04/16
1.5K0
Abp vnext构建API接口服务
版本化ASP.NET Core WebApi
大部分使用Web API的网站都会使用版本化Web API,这是因为当我们升级Web API的时候并不能保证所有的客户端使用的都是最新的Web API,有些旧版本的客户端很有可能使用的某个甚至某几个在新的Web API 中已经废除的接口。如果这时我们部署新的Web API的话,旧版本的客户端就无法使用了。因此Web API 的版本化就出来了。下面我们就来一步一步的实现Web API的版本化。 首先我们需要引如Microsoft.AspNetCore.Mvc.Versioning包,通过 Visual Studio的nuget安装即可。当包成功添加到项目之后,就需要在 Startup的ConfigureServices方法中将ApiVersioning注入到容器中,代码如下:
喵叔
2021/11/17
4670
ABP微服务学习系列-修复System.Text.Json不支持序列化Exception
这是由于接口抛出异常,但是System.Text.Json不支持序列化Exception导致的。 如何解决呢,这里我们需要自己自定义一个ExceptionJsonConverter 在Shared.Hosting.AspNetCore添加JsonConverters目录,新建一个ExceptionJsonConverter类。 类继承JsonConverter泛型接口,并重写他的方法。 完整converter代码如下:
饭勺oO
2023/10/18
4300
ABP微服务学习系列-修复System.Text.Json不支持序列化Exception
相关推荐
ABP微服务系列学习-搭建自己的微服务结构(三)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验