0

I have an ASP.NET Web API I wrote and have published. Now that its out there we are looking at doing some improvements, and these improvements involve changes to certain calls which means we need to version to keep existing clients working.

I have used attribute routing so far in my app. Methods are invoked by: Controller/Action via RoutePrefix and Route attributes.

When I do need to create a V2 of my classes, I only want to recreate the classes that have actually changed, and redirect other routes back to v1 classes because they haven't changed. (Otherwise I just end up with a lot of boilerplate code, or duplicate code).

What I want to do is have the following routes work for my v1 version of classes: Controller/Action

For V2 I want any new classes to go to V2, and any classes that haven't changed I want to return the HttpControllerDescriptor from V1 class. The route would look like v2/Controller/Action but would be redirected to Controller/Action.

I've implemented a IHttpControllerSelector and return the appropriate HttpControllerDescriptors but its not making the call into the method. I believe its because the routing information doesn't match the action. (When I put in an IHttpActionSelector and trace the exception it says "multiple actions were found that match the request).

So, I'm guess I'm wondering: Is this even possible? Is this the best way to achieve what I'm trying to do?

Iain Smith
  • 9,230
  • 4
  • 50
  • 61
James Scott
  • 990
  • 2
  • 10
  • 29
  • This might help you :http://www.culbertsonexchange.com/wp/?p=318 . If you want to achieve what you have mentioned in your question you'll have to implement your own custom logic for attribute routing. – Jose Francis Nov 08 '16 at 12:43

1 Answers1

0

Here is what I implemented for versioning support in asp.net web api. Important to note I did not use attribute routing but explicit routes in WebApiConfig.cs so if you want to follow this pattern you would need to switch back to explicit routes. Also I do not prefer version information in the actual route, I use a custom (ie. "version") parameter in Accept header. I also set the version per mime type as in the below example. If version number is not set by the client or if the requested version does not exist this will fall back to default controller.

  1. Create a class and inherit from DefaultHttpControllerSelector so you can fallback to base class behavior when you wanted to.

  2. Override SelectController method as such:

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request) {

IDictionary controllers = GetControllerMapping(); IHttpRouteData routeData = request.GetRouteData();

                string controllerName = (string)routeData.Values["controller"];

                HttpControllerDescriptor controllerDescriptor;

                if (string.IsNullOrWhiteSpace(controllerName))
                {
                    return base.SelectController(request);
                }

                if (!controllers.TryGetValue(controllerName, out controllerDescriptor))
                {
                    return null;
                }

                string version = GetVersionFromAcceptHeader(request);
                if (string.Equals(version, "1"))
                {
                    return controllerDescriptor;
                }

                string newName = string.Concat(controllerName, "V", version);

                HttpControllerDescriptor versionedControllerDescriptor;
                if (controllers.TryGetValue(newName, out versionedControllerDescriptor))
                {
                    return versionedControllerDescriptor;
                }           

                return controllerDescriptor;
            }
  1. Register this controller selector in your webapiconfig Register method:

    config.Services.Replace(typeof(IHttpControllerSelector), new YourControllerSelector(config));

Dogu Arslan
  • 3,292
  • 24
  • 43