Overview
Codebelt.Extensions.Asp.Versioning is the repository's single package. It keeps the existing REST-oriented registration layer for Asp.Versioning and adds semantic API version support for APIs that need patch numbers, prerelease identifiers, or build metadata as part of the public contract.
It remains focused on integration rather than replacement: service registration, Accept-header filtering, ApiExplorer formatting, semantic version parsing, and problem-details translation are meant to work together instead of being assembled from separate custom hooks.
Key APIs
AddRestfulApiVersioning is the main entry point. It wires AddApiVersioning, MVC conventions, and ApiExplorer registration together, applies the REST-oriented Accept-header reader, and carries the configured default version and selector into Asp.Versioning.
RestfulApiVersioningOptions controls that registration surface. It keeps the reader configuration from earlier releases, accepts SemanticApiVersion values as the default version or convention payload, and exposes the UseBuiltInRfc7807 switch that decides whether problem-details responses stay on ASP.NET Core's default path or are translated into Cuemon HTTP exceptions.
SemanticApiVersion extends ApiVersion with a patch component, an optional prerelease identifier, and optional build metadata. Equality and hash-code calculation keep the full semantic identity intact, while CompareTo follows Semantic Versioning precedence and intentionally ignores build metadata.
SemanticApiVersionParser and SemanticApiVersionFormatter are the runtime bridge between strings, Asp.Versioning format tokens, and semantic version objects. The parser accepts major.minor.patch[-prerelease][+build] values, and the formatter keeps tokens such as VVV and VVVV working so ApiExplorer group naming can still collapse semantic versions to the expected major-version buckets.
SemanticApiVersionAttribute and MapToSemanticApiVersionAttribute expose that version shape in declarative metadata. They let controllers and actions declare semantic versions without dropping down to custom parser plumbing.
SemanticApiVersion.FromVersion and VersionConversionOptions convert four-part System.Version values into semantic versions, optionally moving the revision component into build metadata such as revision.4 or a caller-specified identifier.
RestfulApiVersionReader, UseApiVersionSelector<T>(), and UseRestfulApiVersioning remain the package's boundary with the HTTP pipeline. The reader filters version candidates from the Accept header, the selector shortcut stores the concrete IApiVersionSelector implementation to instantiate later, and the middleware/problem-details path translates versioning failures into HttpStatusCodeException variants that the rest of the API can handle consistently.
Basic usage
using System;
using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Codebelt.Extensions.Asp.Versioning;
using Codebelt.Extensions.Xunit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace MyProject.Tests;
public class SemanticRestfulApiVersioningExample : Test
{
public SemanticRestfulApiVersioningExample(ITestOutputHelper output) : base(output)
{
}
[Fact]
public void AddRestfulApiVersioning_SemanticDefaultVersionConfigured_FormatsExplorerGroupByMajorVersion()
{
var services = new ServiceCollection();
services.AddRestfulApiVersioning(options =>
{
options.DefaultApiVersion = SemanticApiVersion.FromVersion(new Version(1, 2, 3, 4), conversion =>
{
conversion.IncludeRevision = true;
});
});
using var provider = services.BuildServiceProvider();
var apiVersioning = provider.GetRequiredService<IOptions<ApiVersioningOptions>>().Value;
var apiExplorer = provider.GetRequiredService<IOptions<ApiExplorerOptions>>().Value;
var reader = Assert.IsType<RestfulApiVersionReader>(apiVersioning.ApiVersionReader);
var semanticDefault = Assert.IsType<SemanticApiVersion>(apiVersioning.DefaultApiVersion);
TestOutput.WriteLine($"Semantic version: {semanticDefault}");
TestOutput.WriteLine($"Explorer group: {semanticDefault.ToString(apiExplorer.GroupNameFormat)}");
TestOutput.WriteLine($"Accepted media types: {string.Join(", ", reader.ValidAcceptHeaders)}");
Assert.Equal("1.2.3+revision.4", semanticDefault.ToString());
Assert.Equal("'v'VVV", apiExplorer.GroupNameFormat);
Assert.Equal("v1", semanticDefault.ToString(apiExplorer.GroupNameFormat));
Assert.Contains("application/json", reader.ValidAcceptHeaders);
Assert.Equal(semanticDefault, apiExplorer.DefaultApiVersion);
}
}
Use this pattern when a four-part build version needs to become semantic API metadata without losing the package's REST-oriented registration defaults. The example shows that one registration call can keep the Accept-header reader in place while still formatting the semantic version into the major-version ApiExplorer group name that Asp.Versioning expects.
Usage guidance
Use this package when your API exposes versions through a media-type parameter on the Accept header and the version itself carries patch, prerelease, or build metadata that should remain visible to conventions, formatters, and tooling. If the default Asp.Versioning ApiVersion model already fits your contract, or if you do not want the package's error-translation and media-type filtering opinions, configure Asp.Versioning directly instead of adding this extension layer.
