Skip to main content

version-http-apis

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).

Source facts

Repository
abpframework/abp-skills
Last source activity
July 20, 2026 at 03:24
Detected SKILL.md language
English
Stars
23
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
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](https://github.com/dotnet/aspnet-api-versioning/wiki) 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: ```csharp 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). ```csharp public override void ConfigureServices(ServiceConfigurationContext context) { // Show neutral / versionless APIs. 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: ```csharp [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: ```csharp 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): ```csharp 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`: ```csharp var current = serviceProvider.GetRequiredService<ICurrentApiVersionInfo>(); var bookService = serviceProvider.GetRequiredService<IBookV4AppService>(); using (current.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0"))) { var book = await bookService.GetAsync(); // proxy adds ?api-version=4.0 } ``` - `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.
View on GitHub