mirror of https://github.com/abpframework/abp.git
2 changed files with 353 additions and 0 deletions
@ -0,0 +1,349 @@ |
|||
# API Versioning System |
|||
|
|||
ABP Framework integrates the [ASPNET-API-Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki) feature and adapts to C# and JavaScript Static Client Proxies and [Auto API Controller](API/Auto-API-Controllers.md). |
|||
|
|||
|
|||
## Enable API Versioning |
|||
|
|||
```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# and JavaScript Static Client Proxies |
|||
|
|||
This feature does not compatible with [URL Path Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path), I suggest you always use [Versioning-via-the-Query-String](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-Query-String). |
|||
|
|||
### Example |
|||
|
|||
**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); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**Generated CS and JS proxies:** |
|||
|
|||
```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)); |
|||
}; |
|||
|
|||
})(); |
|||
``` |
|||
|
|||
|
|||
## Manually change version |
|||
|
|||
If an application service class supports multiple versions. You can inject `ICurrentApiVersionInfo` to switch versions in C#. |
|||
|
|||
```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); |
|||
} |
|||
``` |
|||
|
|||
We have made a default version in the JS proxy. Of course, you can also manually change the version. |
|||
|
|||
```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 |
|||
``` |
|||
|
|||
## Auto API Controller |
|||
|
|||
```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(); |
|||
} |
|||
``` |
|||
|
|||
## Custom multi-version API controller. |
|||
|
|||
ABP Framework will not affect to your APIs, you can freely implement your APIs according to Microsoft's documentation. |
|||
|
|||
Get more from https://github.com/dotnet/aspnet-api-versioning/wiki |
|||
|
|||
|
|||
## Sample source code |
|||
|
|||
You can get the complete sample source code in https://github.com/abpframework/abp-samples/tree/master/Api-Versioning |
|||
Loading…
Reference in new issue