| name | version-http-apis |
| description | Add API versioning to ABP HTTP APIs so multiple versions of the same service coexist behind static client proxies. USE FOR: AddAbpApiVersioning, query/header/media-type version readers, attaching ApiVersions to conventional controllers, ICurrentApiVersionInfo runtime version switching, the versioned API explorer. DO NOT USE FOR: exposing services as controllers in the first place (use expose-http-apis); Swagger UI / OpenAPI doc setup (use configure-swagger-openapi); generating or calling C# client proxies (use consume-remote-services).
|
| license | MIT |
Version ABP HTTP APIs
ABP integrates the ASP.NET API Versioning
library and adapts it to Auto (Conventional) API Controllers and the static C# / JavaScript
client proxies. You enable it once, then either attach versions per controller (attribute
routing) or per assembly (conventional controllers).
When to Use
- Serving
1.0 and 2.0 (etc.) of the same service side by side.
- Attaching versions to conventional controllers via
ConventionalControllers.Create(...).
- Switching the requested version from C# at runtime with
ICurrentApiVersionInfo.
- Building one Swagger document per discovered version.
When Not to Use
- Just exposing a service as a controller (no versions yet) — use expose-http-apis.
- Swagger UI / OpenAPI wiring beyond the versioned explorer — use configure-swagger-openapi.
- Generating or calling client proxies — use consume-remote-services.
⚠️ Read this first: URL-path versioning breaks the static proxies
The static C# and JavaScript client proxies cannot carry the version in the URL path/segment.
Use query-string versioning (?api-version=2.0) — the default ABP proxies emit.
Grounding: ApiVersionInfo.ShouldSendInQueryString() returns !BindingSource.IsIn("Path")
(framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ApiVersionInfo.cs),
so a Path binding source is never appended to the proxy request. The versioning doc says the
same: this feature "does not compatible with URL Path Versioning, we suggest to use Versioning
via the Query String" (docs/en/framework/api-development/versioning.md).
Enable versioning
AddAbpApiVersioning is an IServiceCollection extension in Microsoft.Extensions.DependencyInjection
(framework/src/Volo.Abp.AspNetCore.Mvc/.../AbpApiVersioningExtensions.cs). Its signature:
IApiVersioningBuilder AddAbpApiVersioning(
this IServiceCollection services,
Action<ApiVersioningOptions>? apiVersioningOptionsSetupAction = null,
Action<MvcApiVersioningOptions>? mvcApiVersioningOptionsSetupAction = null)
ApiVersioningOptions / MvcApiVersioningOptions and the properties set below
(ReportApiVersions, AssumeDefaultVersionWhenUnspecified) come from the ASP.NET API
Versioning library — they are not ABP types. Same for IApiControllerFilter /
NoControllerFilter (used to keep neutral/versionless APIs visible).
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<IApiControllerFilter, NoControllerFilter>();
context.Services.AddAbpApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
The version reader (query string vs. header vs. media type) is the standard ASP.NET API
Versioning ApiVersionReader on ApiVersioningOptions — keep it on the query string when static
proxies are in play (see the warning above).
Attribute-routed controllers
Put [ApiVersion(...)] (an ASP.NET API Versioning attribute) on each controller. Two controllers
sharing one [Route] but different versions serve as v1/v2 of the same service:
[ApiVersion("1.0", Deprecated = true)]
[ControllerName("Book")]
[Route("api/BookStore/Book")]
public class BookController : BookStoreController, IBookAppService { }
[ApiVersion("2.0")]
[ControllerName("Book")]
[Route("api/BookStore/Book")]
public class BookV2Controller : BookStoreController, IBookV2AppService { }
Conventional (Auto API) controllers
Attach versions per assembly in PreConfigureServices. ConventionalControllerSetting.ApiVersions
is a List<ApiVersion> (framework/src/Volo.Abp.AspNetCore.Mvc/.../Conventions/ConventionalControllerSetting.cs);
ApiVersion is an ASP.NET API Versioning type. Use TypePredicate to split versions by namespace:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == typeof(v2.TodoAppService).Namespace;
opts.ApiVersions.Add(new ApiVersion(2, 0));
});
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == typeof(v1.TodoAppService).Namespace;
opts.ApiVersions.Add(new ApiVersion(1, 0));
});
});
}
Then wire those pre-configured settings into the versioning options with the ConfigureAbp
extension — ConfigureAbp(this MvcApiVersioningOptions options, AbpAspNetCoreMvcOptions mvcOptions)
(same file as AddAbpApiVersioning). It reads mvcOptions.ConventionalControllers, calling each
setting's MvcApiVersioningConfigurer if set, otherwise applying its ApiVersions by convention
(neutral when none):
public override void ConfigureServices(ServiceConfigurationContext context)
{
var preActions = context.Services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>();
context.Services.AddTransient<IApiControllerFilter, NoControllerFilter>();
context.Services.AddAbpApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
}, options =>
{
options.ConfigureAbp(preActions.Configure());
});
}
Switch the requested version at runtime (C#)
When one client wants a specific version from a multi-version service, inject
ICurrentApiVersionInfo (namespace Volo.Abp.Http.Client.ClientProxying) and scope the change
with Change(...). It stores an AsyncLocal value restored on dispose, so wrap it in a using:
var current = serviceProvider.GetRequiredService<ICurrentApiVersionInfo>();
var bookService = serviceProvider.GetRequiredService<IBookV4AppService>();
using (current.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0")))
{
var book = await bookService.GetAsync();
}
ApiVersionInfo(string bindingSource, string version) —
framework/src/Volo.Abp.Http.Client/.../ClientProxying/ApiVersionInfo.cs.
ParameterBindingSources.Query / .Header / .Path … are string consts in
Volo.Abp.Http.ProxyScripting.Generators.ParameterBindingSources. Use Query; Path is
dropped by the proxy (see the warning).
Versioned API explorer / Swagger
Chain .AddApiExplorer(...) on the builder returned by AddAbpApiVersioning for one Swagger
document per version, then iterate provider.ApiVersionDescriptions to add a SwaggerEndpoint
each. GroupNameFormat, SubstituteApiVersionInUrl, IApiVersionDescriptionProvider are ASP.NET
API Versioning types (not ABP); full example in the versioning doc. Deeper Swagger setup belongs
to the configure-swagger-openapi skill.
Validation
- Call the same route with
?api-version=1.0 and ?api-version=2.0; confirm each hits the
matching version and that ReportApiVersions adds api-supported-versions response headers.
- Confirm neutral/versionless endpoints still appear (that's the
NoControllerFilter job).
- With generated static proxies, confirm the request carries the version on the query string,
not the path.
Common Pitfalls
- URL-path/segment versioning silently breaks static proxies — the proxy never sends a
Path
binding source. Stay on query-string versioning.
- Setting
ApiVersions on a conventional-controller assembly does nothing unless
options.ConfigureAbp(preActions.Configure()) is applied to the versioning options.
AddAbpApiVersioning returns an IApiVersioningBuilder; call .AddApiExplorer(...) on that
return value — don't try to add the explorer separately.
ReportApiVersions, AssumeDefaultVersionWhenUnspecified, [ApiVersion], ApiVersion,
IApiControllerFilter/NoControllerFilter are ASP.NET API Versioning symbols ABP wraps — look
them up in that library's wiki, not the ABP API.