I've been trying to implement versioned routing in Web API 2.2 (5.2.3) where a newer version of a controller has priority over previous versions when selecting an action for a specific route.
e.g. Given the following controller class hierarcy:
namespace MyApi.Controllers.V1 {
public class FooController {
protected IFooRepository Repo; // Assume Repo is injected via IoC
[Route("{id:int:min(1)}")]
public IFoo GetSingle(int id) {
Repo.Single(new FooSearchOpts { Id = id });
}
public IEnumerable<IFoo> GetAll() {
Repo.Search();
}
}
}
namespace MyApi.Controllers.V3 {
public class FooController : V1.FooController {
public IEnumerable<IFoo> Search(string type) {
Repo.Search(new FooSearchOpts { Type = type });
}
}
}
namespace MyApi.Controllers.V4 {
public class FooController : V3.FooController {
public IEnumerable<IFoo> SearchV4(string type, string name) {
Repo.Search(new FooSearchOpts {
Type = type,
Name = name
});
}
}
}
namespace MyApi.Controllers.V5 {
public class FooController : V4.FooController {
/* nothing relevant */
}
}
I can get some routes working by creating a custom IHttpControllerSelector that reads {version} from the route data (e.g. GET /api/v*/foo/123 always points to GetSingle).
The problem appears when there are multiple possibles routes in different versions of the controller. I need the request GET /api/v*/foo to choose the following methods:
/api/v1/foo -> V1.FooController.GetAll
/api/v2/foo -> V1.FooController.GetAll
/api/v3/foo -> V3.FooController.Search
/api/v4/foo -> V4.FooController.SearchV4
/api/v5/foo -> V5.FooController.SearchV4
Just specifying a custom controller selector understandibly complains about duplicate/ambiguous actions, but I can't find any way to retain the existing action selection logic when I specify a custom IHttpActionSelector. Do I really need to reimplement all the method name parsing and parameter checking shenanigans by hand or is there a way to leverage the existing logic?
Unfortunately I can't post all of what I have already but the main routing config looks something like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// VersionedRouteProvider specifies a global route prefix of "api/v{version}/{ctrl}"
config.MapHttpAttributeRoutes(new VersionedRouteProvider());
// VersionedControllerSelector uses {version} and {ctrl} to map out controllers
var controllerSelector = new VersionedControllerSelector(config);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
// VersionedActionSelector creates actions from controllers,
// currently it would require iterating MethodInfos
// and manually creating all HttpActionDescriptors
// based on the methods' names and parameters
config.Services.Replace(typeof(IHttpActionSelector), new VersionedActionSelector(controllerSelector));
}
}
I've looked into creating a custom IDirectRouteProvider and IDirectRouteFactory but it seems like I'd still need a custom IHttpActionSelector to handle ambiguous routes.
Thanks.