Description
TL;DR: It seems the the AssumeDefaultVersionWhenUnspecified is not working with RoutePrefix and ByNamespace versioning. It might be too complex for the routing system to handle this combinations but would be definitly great if it is supported.
Long Version:
Consider the ByNamespaceWebApiSample project as a starting point and let us combine it with attribute routing:
namespace Microsoft.Examples.V1.Controllers
{
[ApiVersion( "1.0" )]
[RoutePrefix( "v{version:apiVersion}/agreements" )]
public class AgreementsController : ApiController { [Route("{accountId}")] public IHttpActionResult Get( string accountId ) ... }
}
namespace Microsoft.Examples.V2.Controllers
{
[ApiVersion( "2.0" )]
[RoutePrefix( "v{version:apiVersion}/agreements" )]
public class AgreementsController : ApiController { [Route("{accountId}")] public IHttpActionResult Get( string accountId ) ... }
}
namespace Microsoft.Examples.V3.Controllers
{
[ApiVersion( "3.0" )]
[RoutePrefix( "v{version:apiVersion}/agreements" )]
public class AgreementsController : ApiController { [Route("{accountId}")] public IHttpActionResult Get( string accountId ) ... }
}
And we change the Startup do use the attributes instead of fixed routes:
var constraintResolver = new DefaultInlineConstraintResolver() { ConstraintMap = { ["apiVersion"] = typeof(ApiVersionRouteConstraint) } };
var configuration = new HttpConfiguration();
var httpServer = new HttpServer( configuration );
configuration.AddApiVersioning(o =>
{
o.ReportApiVersions = true;
o.DefaultApiVersion = new ApiVersion(3, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
} );
configuration.MapHttpAttributeRoutes(constraintResolver);
builder.UseWebApi( httpServer );
Then it is possible to call the different versions via the explicit version:
- v1/agreements/4711 -> {"Controller":"Microsoft.Examples.V1.Controllers.AgreementsController","AccountId":"test","ApiVersion":"1"}
- v1.0/agreements/4711 -> {"Controller":"Microsoft.Examples.V1.Controllers.AgreementsController","AccountId":"test","ApiVersion":"1.0"}
- v2/agreements/4711 -> {"Controller":"Microsoft.Examples.V2.Controllers.AgreementsController","AccountId":"test","ApiVersion":"2"}
- v2.0/agreements/4711 -> {"Controller":"Microsoft.Examples.V2.Controllers.AgreementsController","AccountId":"test","ApiVersion":"2.0"}
- v3/agreements/4711 -> {"Controller":"Microsoft.Examples.V3.Controllers.AgreementsController","AccountId":"test","ApiVersion":"3"}
- v3.0/agreements/4711 -> {"Controller":"Microsoft.Examples.V3.Controllers.AgreementsController","AccountId":"test","ApiVersion":"3.0"}
But if you try to call the latest version by removing the version, you only get an error 404:
- agreements/4711 -> HTTP Error 404.0 - Not Found instead of {"Controller":"Microsoft.Examples.V3.Controllers.AgreementsController","AccountId":"test","ApiVersion":"3.0"}
Is there a way to have this combination? From code perspective the by namespace is simply the cleanest way to versionize your API. We also prefer the url segment based versioning due to
various reasons (documentation with swagger and swagger UI, browser/html-form compatibility,...). But still we want to provide also a default endpoint for the current version.