ABP版本控制系统

ABP框架集成了ASPNET-API-版本控制功能并适配C#和JavaScript静态代理和自动API控制器.

启用API版本控制

  1. public override void ConfigureServices(ServiceConfigurationContext context)
  2. {
  3. context.Services.AddAbpApiVersioning(options =>
  4. {
  5. // Show neutral/versionless APIs.
  6. options.UseApiBehavior = false;
  7. options.ReportApiVersions = true;
  8. options.AssumeDefaultVersionWhenUnspecified = true;
  9. });
  10. Configure<AbpAspNetCoreMvcOptions>(options =>
  11. {
  12. options.ChangeControllerModelApiExplorerGroupName = false;
  13. });
  14. }

C# 和 JavaScript 静态客户端代理

这个功能不兼容URL路径版本控制, 建议你始终使用Query-String版本控制

示例

Application Services:

  1. public interface IBookAppService : IApplicationService
  2. {
  3. Task<BookDto> GetAsync();
  4. }
  5. public interface IBookV2AppService : IApplicationService
  6. {
  7. Task<BookDto> GetAsync();
  8. Task<BookDto> GetAsync(string isbn);
  9. }

HttpApi Controillers:

  1. [Area(BookStoreRemoteServiceConsts.ModuleName)]
  2. [RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)]
  3. [ApiVersion("1.0", Deprecated = true)]
  4. [ApiController]
  5. [ControllerName("Book")]
  6. [Route("api/BookStore/Book")]
  7. public class BookController : BookStoreController, IBookAppService
  8. {
  9. private readonly IBookAppService _bookAppService;
  10. public BookController(IBookAppService bookAppService)
  11. {
  12. _bookAppService = bookAppService;
  13. }
  14. [HttpGet]
  15. public async Task<BookDto> GetAsync()
  16. {
  17. return await _bookAppService.GetAsync();
  18. }
  19. }
  20. [Area(BookStoreRemoteServiceConsts.ModuleName)]
  21. [RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)]
  22. [ApiVersion("2.0")]
  23. [ApiController]
  24. [ControllerName("Book")]
  25. [Route("api/BookStore/Book")]
  26. public class BookV2Controller : BookStoreController, IBookV2AppService
  27. {
  28. private readonly IBookV2AppService _bookAppService;
  29. public BookV2Controller(IBookV2AppService bookAppService)
  30. {
  31. _bookAppService = bookAppService;
  32. }
  33. [HttpGet]
  34. public async Task<BookDto> GetAsync()
  35. {
  36. return await _bookAppService.GetAsync();
  37. }
  38. [HttpGet]
  39. [Route("{isbn}")]
  40. public async Task<BookDto> GetAsync(string isbn)
  41. {
  42. return await _bookAppService.GetAsync(isbn);
  43. }
  44. }

生成 CS 和 JS 代理:

  1. [Dependency(ReplaceServices = true)]
  2. [ExposeServices(typeof(IBookAppService), typeof(BookClientProxy))]
  3. public partial class BookClientProxy : ClientProxyBase<IBookAppService>, IBookAppService
  4. {
  5. public virtual async Task<BookDto> GetAsync()
  6. {
  7. return await RequestAsync<BookDto>(nameof(GetAsync));
  8. }
  9. }
  10. [Dependency(ReplaceServices = true)]
  11. [ExposeServices(typeof(IBookV2AppService), typeof(BookV2ClientProxy))]
  12. public partial class BookV2ClientProxy : ClientProxyBase<IBookV2AppService>, IBookV2AppService
  13. {
  14. public virtual async Task<BookDto> GetAsync()
  15. {
  16. return await RequestAsync<BookDto>(nameof(GetAsync));
  17. }
  18. public virtual async Task<BookDto> GetAsync(string isbn)
  19. {
  20. return await RequestAsync<BookDto>(nameof(GetAsync), new ClientProxyRequestTypeValue
  21. {
  22. { typeof(string), isbn }
  23. });
  24. }
  25. }
  1. // controller bookStore.books.book
  2. (function(){
  3. abp.utils.createNamespace(window, 'bookStore.books.book');
  4. bookStore.books.book.get = function(api_version, ajaxParams) {
  5. var api_version = api_version ? api_version : '1.0';
  6. return abp.ajax($.extend(true, {
  7. url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
  8. type: 'GET'
  9. }, ajaxParams));
  10. };
  11. })();
  12. // controller bookStore.books.bookV2
  13. (function(){
  14. abp.utils.createNamespace(window, 'bookStore.books.bookV2');
  15. bookStore.books.bookV2.get = function(api_version, ajaxParams) {
  16. var api_version = api_version ? api_version : '2.0';
  17. return abp.ajax($.extend(true, {
  18. url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
  19. type: 'GET'
  20. }, ajaxParams));
  21. };
  22. bookStore.books.bookV2.getAsyncByIsbn = function(isbn, api_version, ajaxParams) {
  23. var api_version = api_version ? api_version : '2.0';
  24. return abp.ajax($.extend(true, {
  25. url: abp.appPath + 'api/BookStore/Book/' + isbn + '' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
  26. type: 'GET'
  27. }, ajaxParams));
  28. };
  29. })();

手动更改版本

如果应用服务支持多版本, 你可以注入 ICurrentApiVersionInfo 来切换版本.

  1. var currentApiVersionInfo = _abpApplication.ServiceProvider.GetRequiredService<ICurrentApiVersionInfo>();
  2. var bookV4AppService = _abpApplication.ServiceProvider.GetRequiredService<IBookV4AppService>();
  3. using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0")))
  4. {
  5. book = await bookV4AppService.GetAsync();
  6. logger.LogWarning(book.Title);
  7. logger.LogWarning(book.ISBN);
  8. }
  9. using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.1")))
  10. {
  11. book = await bookV4AppService.GetAsync();
  12. logger.LogWarning(book.Title);
  13. logger.LogWarning(book.ISBN);
  14. }

在JS代理中有一个默认版本, 当然你也可以手动更改.

  1. bookStore.books.bookV4.get("4.0") // Manually change the version.
  2. //Title: Mastering ABP Framework V4.0
  3. bookStore.books.bookV4.get() // The latest supported version is used by default.
  4. //Title: Mastering ABP Framework V4.1

自动API控制器

  1. public override void PreConfigureServices(ServiceConfigurationContext context)
  2. {
  3. PreConfigure<AbpAspNetCoreMvcOptions>(options =>
  4. {
  5. //2.0 Version
  6. options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
  7. {
  8. opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v2.TodoAppService).Namespace;
  9. opts.ApiVersions.Add(new ApiVersion(2, 0));
  10. });
  11. //1.0 Compatibility version
  12. options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
  13. {
  14. opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v1.TodoAppService).Namespace;
  15. opts.ApiVersions.Add(new ApiVersion(1, 0));
  16. });
  17. });
  18. }
  19. public override void ConfigureServices(ServiceConfigurationContext context)
  20. {
  21. var preActions = context.Services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>();
  22. Configure<AbpAspNetCoreMvcOptions>(options =>
  23. {
  24. preActions.Configure(options);
  25. });
  26. context.Services.AddAbpApiVersioning(options =>
  27. {
  28. // Show neutral/versionless APIs.
  29. options.UseApiBehavior = false;
  30. options.ReportApiVersions = true;
  31. options.AssumeDefaultVersionWhenUnspecified = true;
  32. options.ConfigureAbp(preActions.Configure());
  33. });
  34. Configure<AbpAspNetCoreMvcOptions>(options =>
  35. {
  36. options.ChangeControllerModelApiExplorerGroupName = false;
  37. });
  38. }

Swagger/VersionedApiExplorer

  1. public override void ConfigureServices(ServiceConfigurationContext context)
  2. {
  3. context.Services.AddAbpApiVersioning(options =>
  4. {
  5. // Show neutral/versionless APIs.
  6. options.UseApiBehavior = false;
  7. options.ReportApiVersions = true;
  8. options.AssumeDefaultVersionWhenUnspecified = true;
  9. });
  10. context.Services.AddVersionedApiExplorer(
  11. options =>
  12. {
  13. // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
  14. // note: the specified format code will format the version as "'v'major[.minor][-status]"
  15. options.GroupNameFormat = "'v'VVV";
  16. // note: this option is only necessary when versioning by url segment. the SubstitutionFormat
  17. // can also be used to control the format of the API version in route templates
  18. options.SubstituteApiVersionInUrl = true;
  19. });
  20. context.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
  21. context.Services.AddAbpSwaggerGen(
  22. options =>
  23. {
  24. // add a custom operation filter which sets default values
  25. options.OperationFilter<SwaggerDefaultValues>();
  26. options.CustomSchemaIds(type => type.FullName);
  27. });
  28. Configure<AbpAspNetCoreMvcOptions>(options =>
  29. {
  30. options.ChangeControllerModelApiExplorerGroupName = false;
  31. });
  32. }
  33. public override void OnApplicationInitialization(ApplicationInitializationContext context)
  34. {
  35. var app = context.GetApplicationBuilder();
  36. var env = context.GetEnvironment();
  37. if (env.IsDevelopment())
  38. {
  39. app.UseDeveloperExceptionPage();
  40. }
  41. else
  42. {
  43. app.UseErrorPage();
  44. app.UseHsts();
  45. }
  46. app.UseHttpsRedirection();
  47. app.UseStaticFiles();
  48. app.UseRouting();
  49. app.UseAbpRequestLocalization();
  50. app.UseSwagger();
  51. app.UseSwaggerUI(
  52. options =>
  53. {
  54. var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>();
  55. // build a swagger endpoint for each discovered API version
  56. foreach (var description in provider.ApiVersionDescriptions)
  57. {
  58. options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
  59. }
  60. });
  61. app.UseConfiguredEndpoints();
  62. }

自定义多版本API控制器

ABP框架不会影响你的API, 你可以根据微软文档自由的实现你的API.

参阅: https://github.com/dotnet/aspnet-api-versioning/wiki

示例源码

你可以在这里得到完整的示例源码: https://github.com/abpframework/abp-samples/tree/master/Api-Versioning