Overview
在前面的文章里有針對 abp 的專案模板進行簡化,構建了一個精簡的專案模板,在使用程序中,因為我們暴露的 api 需要包含版本資訊,我們采取的方式是將 api 的版本號包含在資源的 URI 中,因為 abp 默認的 api 是沒有版本的概念的,所以這里為了實作 api 版本化需要針對 abp 專案的 api 路由進行改造,從而滿足我們的需求,本篇文章則是實作這一改造程序的演示說明,希望可以對你有所幫助
完整的專案模板如下所示
模板原始碼地址:https://github.com/danvic712/ingos-abp-api-template
Step by Step
在 abp 專案中,可以通過如下的兩種方式實作 api 介面的定義
- 傳統的 web api 實作方式,通過定義 controller 來完成資源 api 構建
- 通過 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 添加對于這兩個程式集的參考
## 添加 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 特性,然后修改特性路由即可,示例代碼如下所示
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class VaulesController : ControllerBase
{
// action ...
}
而對于 abp 基于 application service 自動生成的 api,在默認的專案模板中,你可以在 *HttpApiHostModule 類中找到如下的配置,最終可以生成下圖中的 api 路由格式
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
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
[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 方法執行之前執行,調整后的代碼如下
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 暴露出來
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 中,這里的配置項和之前的方式一樣就不做解釋了,完善后的方法代碼如下所示
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 相關的配置代碼如下
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 = "[email protected]",
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 上進行查看,最終實作效果如下所示

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/275638.html
標籤:.NET技术
下一篇:VS中設定版本號自動遞增
