mirror of https://github.com/abpframework/abp.git
2 changed files with 351 additions and 0 deletions
@ -0,0 +1,347 @@ |
|||||
|
# ABP版本控制系统 |
||||
|
|
||||
|
ABP框架集成了[ASPNET-API-版本控制](https://github.com/dotnet/aspnet-api-versioning/wiki)功能并适配C#和JavaScript静态代理和[自动API控制器](API/Auto-API-Controllers.md). |
||||
|
|
||||
|
## 启用API版本控制 |
||||
|
|
||||
|
```cs |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAbpApiVersioning(options => |
||||
|
{ |
||||
|
// Show neutral/versionless APIs. |
||||
|
options.UseApiBehavior = false; |
||||
|
|
||||
|
options.ReportApiVersions = true; |
||||
|
options.AssumeDefaultVersionWhenUnspecified = true; |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpAspNetCoreMvcOptions>(options => |
||||
|
{ |
||||
|
options.ChangeControllerModelApiExplorerGroupName = false; |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## C# 和 JavaScript 静态客户端代理 |
||||
|
|
||||
|
这个功能不兼容[URL路径版本控制](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path), 建议你始终使用[Query-String版本控制](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-Query-String) |
||||
|
|
||||
|
### 示例 |
||||
|
|
||||
|
**Application Services:** |
||||
|
|
||||
|
```cs |
||||
|
public interface IBookAppService : IApplicationService |
||||
|
{ |
||||
|
Task<BookDto> GetAsync(); |
||||
|
} |
||||
|
|
||||
|
public interface IBookV2AppService : IApplicationService |
||||
|
{ |
||||
|
Task<BookDto> GetAsync(); |
||||
|
|
||||
|
Task<BookDto> GetAsync(string isbn); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**HttpApi Controillers:** |
||||
|
|
||||
|
```cs |
||||
|
[Area(BookStoreRemoteServiceConsts.ModuleName)] |
||||
|
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)] |
||||
|
[ApiVersion("1.0", Deprecated = true)] |
||||
|
[ApiController] |
||||
|
[ControllerName("Book")] |
||||
|
[Route("api/BookStore/Book")] |
||||
|
public class BookController : BookStoreController, IBookAppService |
||||
|
{ |
||||
|
private readonly IBookAppService _bookAppService; |
||||
|
|
||||
|
public BookController(IBookAppService bookAppService) |
||||
|
{ |
||||
|
_bookAppService = bookAppService; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public async Task<BookDto> GetAsync() |
||||
|
{ |
||||
|
return await _bookAppService.GetAsync(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Area(BookStoreRemoteServiceConsts.ModuleName)] |
||||
|
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)] |
||||
|
[ApiVersion("2.0")] |
||||
|
[ApiController] |
||||
|
[ControllerName("Book")] |
||||
|
[Route("api/BookStore/Book")] |
||||
|
public class BookV2Controller : BookStoreController, IBookV2AppService |
||||
|
{ |
||||
|
private readonly IBookV2AppService _bookAppService; |
||||
|
|
||||
|
public BookV2Controller(IBookV2AppService bookAppService) |
||||
|
{ |
||||
|
_bookAppService = bookAppService; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public async Task<BookDto> GetAsync() |
||||
|
{ |
||||
|
return await _bookAppService.GetAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{isbn}")] |
||||
|
public async Task<BookDto> GetAsync(string isbn) |
||||
|
{ |
||||
|
return await _bookAppService.GetAsync(isbn); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**生成 CS 和 JS 代理:** |
||||
|
|
||||
|
```cs |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(IBookAppService), typeof(BookClientProxy))] |
||||
|
public partial class BookClientProxy : ClientProxyBase<IBookAppService>, IBookAppService |
||||
|
{ |
||||
|
public virtual async Task<BookDto> GetAsync() |
||||
|
{ |
||||
|
return await RequestAsync<BookDto>(nameof(GetAsync)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(IBookV2AppService), typeof(BookV2ClientProxy))] |
||||
|
public partial class BookV2ClientProxy : ClientProxyBase<IBookV2AppService>, IBookV2AppService |
||||
|
{ |
||||
|
public virtual async Task<BookDto> GetAsync() |
||||
|
{ |
||||
|
return await RequestAsync<BookDto>(nameof(GetAsync)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<BookDto> GetAsync(string isbn) |
||||
|
{ |
||||
|
return await RequestAsync<BookDto>(nameof(GetAsync), new ClientProxyRequestTypeValue |
||||
|
{ |
||||
|
{ typeof(string), isbn } |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
```js |
||||
|
// controller bookStore.books.book |
||||
|
|
||||
|
(function(){ |
||||
|
|
||||
|
abp.utils.createNamespace(window, 'bookStore.books.book'); |
||||
|
|
||||
|
bookStore.books.book.get = function(api_version, ajaxParams) { |
||||
|
var api_version = api_version ? api_version : '1.0'; |
||||
|
return abp.ajax($.extend(true, { |
||||
|
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '', |
||||
|
type: 'GET' |
||||
|
}, ajaxParams)); |
||||
|
}; |
||||
|
|
||||
|
})(); |
||||
|
|
||||
|
// controller bookStore.books.bookV2 |
||||
|
|
||||
|
(function(){ |
||||
|
|
||||
|
abp.utils.createNamespace(window, 'bookStore.books.bookV2'); |
||||
|
|
||||
|
bookStore.books.bookV2.get = function(api_version, ajaxParams) { |
||||
|
var api_version = api_version ? api_version : '2.0'; |
||||
|
return abp.ajax($.extend(true, { |
||||
|
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '', |
||||
|
type: 'GET' |
||||
|
}, ajaxParams)); |
||||
|
}; |
||||
|
|
||||
|
bookStore.books.bookV2.getAsyncByIsbn = function(isbn, api_version, ajaxParams) { |
||||
|
var api_version = api_version ? api_version : '2.0'; |
||||
|
return abp.ajax($.extend(true, { |
||||
|
url: abp.appPath + 'api/BookStore/Book/' + isbn + '' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '', |
||||
|
type: 'GET' |
||||
|
}, ajaxParams)); |
||||
|
}; |
||||
|
|
||||
|
})(); |
||||
|
``` |
||||
|
|
||||
|
## 手动更改版本 |
||||
|
|
||||
|
如果应用服务支持多版本, 你可以注入 `ICurrentApiVersionInfo` 来切换版本. |
||||
|
|
||||
|
```cs |
||||
|
var currentApiVersionInfo = _abpApplication.ServiceProvider.GetRequiredService<ICurrentApiVersionInfo>(); |
||||
|
var bookV4AppService = _abpApplication.ServiceProvider.GetRequiredService<IBookV4AppService>(); |
||||
|
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0"))) |
||||
|
{ |
||||
|
book = await bookV4AppService.GetAsync(); |
||||
|
logger.LogWarning(book.Title); |
||||
|
logger.LogWarning(book.ISBN); |
||||
|
} |
||||
|
|
||||
|
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.1"))) |
||||
|
{ |
||||
|
book = await bookV4AppService.GetAsync(); |
||||
|
logger.LogWarning(book.Title); |
||||
|
logger.LogWarning(book.ISBN); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
在JS代理中有一个默认版本, 当然你也可以手动更改. |
||||
|
|
||||
|
```js |
||||
|
|
||||
|
bookStore.books.bookV4.get("4.0") // Manually change the version. |
||||
|
//Title: Mastering ABP Framework V4.0 |
||||
|
|
||||
|
bookStore.books.bookV4.get() // The latest supported version is used by default. |
||||
|
//Title: Mastering ABP Framework V4.1 |
||||
|
``` |
||||
|
|
||||
|
## 自动API控制器 |
||||
|
|
||||
|
```cs |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
PreConfigure<AbpAspNetCoreMvcOptions>(options => |
||||
|
{ |
||||
|
//2.0 Version |
||||
|
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts => |
||||
|
{ |
||||
|
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v2.TodoAppService).Namespace; |
||||
|
opts.ApiVersions.Add(new ApiVersion(2, 0)); |
||||
|
}); |
||||
|
|
||||
|
//1.0 Compatibility version |
||||
|
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts => |
||||
|
{ |
||||
|
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v1.TodoAppService).Namespace; |
||||
|
opts.ApiVersions.Add(new ApiVersion(1, 0)); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
var preActions = context.Services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>(); |
||||
|
Configure<AbpAspNetCoreMvcOptions>(options => |
||||
|
{ |
||||
|
preActions.Configure(options); |
||||
|
}); |
||||
|
|
||||
|
context.Services.AddAbpApiVersioning(options => |
||||
|
{ |
||||
|
// Show neutral/versionless APIs. |
||||
|
options.UseApiBehavior = false; |
||||
|
|
||||
|
options.ReportApiVersions = true; |
||||
|
options.AssumeDefaultVersionWhenUnspecified = true; |
||||
|
|
||||
|
options.ConfigureAbp(preActions.Configure()); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpAspNetCoreMvcOptions>(options => |
||||
|
{ |
||||
|
options.ChangeControllerModelApiExplorerGroupName = false; |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Swagger/VersionedApiExplorer |
||||
|
|
||||
|
```cs |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAbpApiVersioning(options => |
||||
|
{ |
||||
|
// Show neutral/versionless APIs. |
||||
|
options.UseApiBehavior = false; |
||||
|
|
||||
|
options.ReportApiVersions = true; |
||||
|
options.AssumeDefaultVersionWhenUnspecified = true; |
||||
|
}); |
||||
|
|
||||
|
context.Services.AddVersionedApiExplorer( |
||||
|
options => |
||||
|
{ |
||||
|
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service |
||||
|
// note: the specified format code will format the version as "'v'major[.minor][-status]" |
||||
|
options.GroupNameFormat = "'v'VVV"; |
||||
|
|
||||
|
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat |
||||
|
// can also be used to control the format of the API version in route templates |
||||
|
options.SubstituteApiVersionInUrl = true; |
||||
|
}); |
||||
|
|
||||
|
context.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>(); |
||||
|
|
||||
|
context.Services.AddAbpSwaggerGen( |
||||
|
options => |
||||
|
{ |
||||
|
// add a custom operation filter which sets default values |
||||
|
options.OperationFilter<SwaggerDefaultValues>(); |
||||
|
|
||||
|
options.CustomSchemaIds(type => type.FullName); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpAspNetCoreMvcOptions>(options => |
||||
|
{ |
||||
|
options.ChangeControllerModelApiExplorerGroupName = false; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
||||
|
{ |
||||
|
var app = context.GetApplicationBuilder(); |
||||
|
var env = context.GetEnvironment(); |
||||
|
|
||||
|
if (env.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseDeveloperExceptionPage(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
app.UseErrorPage(); |
||||
|
app.UseHsts(); |
||||
|
} |
||||
|
|
||||
|
app.UseHttpsRedirection(); |
||||
|
app.UseStaticFiles(); |
||||
|
app.UseRouting(); |
||||
|
app.UseAbpRequestLocalization(); |
||||
|
|
||||
|
app.UseSwagger(); |
||||
|
app.UseSwaggerUI( |
||||
|
options => |
||||
|
{ |
||||
|
var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>(); |
||||
|
// build a swagger endpoint for each discovered API version |
||||
|
foreach (var description in provider.ApiVersionDescriptions) |
||||
|
{ |
||||
|
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
app.UseConfiguredEndpoints(); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 自定义多版本API控制器 |
||||
|
|
||||
|
ABP框架不会影响你的API, 你可以根据微软文档自由的实现你的API. |
||||
|
|
||||
|
参阅: https://github.com/dotnet/aspnet-api-versioning/wiki |
||||
|
|
||||
|
## 示例源码 |
||||
|
|
||||
|
你可以在这里得到完整的示例源码: https://github.com/abpframework/abp-samples/tree/master/Api-Versioning |
||||
Loading…
Reference in new issue