0

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.

1 Answer 1

1

Versioning should never work this way or you could disable all of your users code, as they will receive different results than they previously did. Versioning should be done with attribute routing (api/v2/whatever) to allow the consumers of your api to switch to the newer version as they choose.

[HttpGet, Route("api/v1/users")
public IHttpActionResult UsersVersion1() {}

[HttpGet, Route("api/v2/users")
public IHttpActionResult UsersVersion2() {}
Sign up to request clarification or add additional context in comments.

1 Comment

The user still specifies the api version in the url explicitly, just as in your example, just the routing is based on namespace and class hierarchy instead of having to manually copy/paste potentially identical methods between versions and having to manually annotate everything with routing attributes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.