4

I need to get the controller name from my route and this I can do if using standard routing code in WebApiConfig.

However, if I am using routing attributes it starts to get a little difficult, especially when trying to version.

Example: If I call an api/terms/bonuses and I have a BonusController and BonusV2Controller and a BonusV3Controller, this code returns the latest controller version 3. That's ok, I can live with that returning the latest and greatest version as a default.

var attributedRoutesData = request.GetRouteData().GetSubRoutes();
var subRouteData = attributedRoutesData.FirstOrDefault();

var actions = (ReflectedHttpActionDescriptor[])subRouteData.Route.DataTokens["actions"];
//This seems to get latest controller name. ie. V2
controllerName = actions[0].ControllerDescriptor.ControllerName;

Now if I request a version 1, for simplicity I'll use a querystring and call api/terms/bonuses?v=2

So this code no longer works (obviously).

How do I get the V2 controller name?

If I abandon routing attributes and just use WebApiConfig routing, this code works happily.

HttpControllerDescriptor controllerDescriptor = null; 
var controllers = GetControllerMapping();
var routeData = request.GetRouteData();
var controllerName = (string)routeData.Values["controller"];

UPDATE:

Here is my full selector code.

IDictionary<string, HttpControllerDescriptor> controllers = GetControllerMapping();                                             

var attributedRoutesData = request.GetRouteData().GetSubRoutes();
var subRouteData = attributedRoutesData.LastOrDefault(); //LastOrDefault() will get PeopleController, FirstOrDefault will get People{version}Controller which we don't want

var actions = (ReflectedHttpActionDescriptor[])subRouteData.Route.DataTokens["actions"];
var controllerName = actions[0].ControllerDescriptor.ControllerName;


//For controller name without attribute routing
//var controllerName = (string)routeData.Values["controller"];

HttpControllerDescriptor oldControllerDescriptor;
if (controllers.TryGetValue(controllerName, out oldControllerDescriptor))
{
    //TODO: Different techniques for handling version api requests.
    var apiVersion = GetVersionFromQueryString(request);
    //var version = GetVersionFromHeader(request);
    //var version = GetVersionFromAcceptHeaderVersion(request);
    //var version = GetVersionFromMediaType(request);

    if (!String.IsNullOrEmpty(apiVersion))
    {
        var newControllerName = String.Concat(controllerName, "V", apiVersion);

        HttpControllerDescriptor newControllerDescriptor;
        if (controllers.TryGetValue(newControllerName, out newControllerDescriptor))
        {
            return newControllerDescriptor;
            }
        }
        return oldControllerDescriptor;
    }
    return null;
Cœur
  • 37,241
  • 25
  • 195
  • 267
richardb
  • 943
  • 1
  • 10
  • 27
  • Could you share your complete implementation of SelectController of your controller selector to get a good idea? – Kiran Jan 17 '14 at 16:29
  • you can also take a look at the following post: http://stackoverflow.com/questions/19835015/versioning-asp-net-web-api-2-with-media-types/19882371#19882371 – Kiran Jan 17 '14 at 16:35
  • 2
    With that UPDATE, I now get an error. {"$id":"1","message":"An error has occurred.","exceptionMessage":"The given key was not present in the dictionary.","exceptionType":"System.Collections.Generic.KeyNotFoundException","stackTrace":" at System.Collections.Generic.Dictionary`2.get_Item(TKey key)\r\n at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.FindActionMatchRequiredRouteAndQueryParameters(IEnumerable`1 candidatesFound)\r\n at ...... – richardb Jan 17 '14 at 16:55

2 Answers2

2
 var subRouteData = request.GetRouteData().GetSubRoutes().LastOrDefault();

 if (subRouteData != null && subRouteData.Route != null)
 {
  var actions = subRouteData.Route.DataTokens["actions"] as HttpActionDescriptor[];

  if (actions != null && actions.Length > 0)
  {
     controllerName = actions[0].ControllerDescriptor.ControllerName;
  }
}
Naveen K
  • 21
  • 2
  • 4
    So, to get this straight. If I want the controller name, while inside the controller, I have to use: `((HttpActionDescriptor[]) Request.GetRouteData().GetSubRoutes().Last().Route.DataTokens["actions"])[0].ControllerDescriptor.ControllerName` At what point would this be appropriately classified as a rube goldberg system? – bob Jun 26 '14 at 14:39
0

At last I found it:

filterContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName
Oren Levy
  • 11
  • 3
  • I would have liked to have seen more clarity on your answer but this does look like it could help someone. It's similar to another answer, but is more concise. – Michael Z. May 20 '21 at 03:21