public class VersionAwareControllerSelector : DefaultHttpControllerSelector
{
private const string VERSION_HEADER_NAME = "some-value";
private const string VERSION_QUERY_NAME = "v";
private HttpConfiguration _configuration;
public VersionAwareControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
_configuration = configuration;
}
// This works for Web API 2 and Attributed Routing
// FROM: http://stackoverflow.com/questions/19835015/versioning-asp-net-web-api-2-with-media-types/19882371#19882371
// BLOG: http://webstackoflove.com/asp-net-web-api-versioning-with-media-types/
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
HttpControllerDescriptor controllerDescriptor = null;
// Get a list of all controllers provided by the default selector
IDictionary<string, HttpControllerDescriptor> controllers = GetControllerMapping();
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
// Pick up the API Version from the header...but we could also do query string
var apiVersion = GetVersionFromHeader(request);
// Check if this route is actually an attribute route
IEnumerable<IHttpRouteData> attributeSubRoutes = routeData.GetSubRoutes();
if (attributeSubRoutes == null)
{
string controllerName = GetRouteVariable<string>(routeData, "controller");
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
string newControllerName = String.Concat(controllerName, apiVersion);
if (controllers.TryGetValue(newControllerName, out controllerDescriptor))
{
return controllerDescriptor;
}
else
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
else
{
string newControllerNameSuffix = String.Concat("V", apiVersion); ;
IEnumerable<IHttpRouteData> filteredSubRoutes = attributeSubRoutes.Where(attrRouteData =>
{
HttpControllerDescriptor currentDescriptor = GetControllerDescriptor(attrRouteData);
bool match = currentDescriptor.ControllerName.EndsWith(newControllerNameSuffix);
if (match && (controllerDescriptor == null))
{
controllerDescriptor = currentDescriptor;
}
return match;
});
routeData.Values["MS_SubRoutes"] = filteredSubRoutes.ToArray();
}
return controllerDescriptor;
}
private HttpControllerDescriptor GetControllerDescriptor(IHttpRouteData routeData)
{
return ((HttpActionDescriptor[])routeData.Route.DataTokens["actions"]).First().ControllerDescriptor;
}
// Get a value from the route data, if present.
private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
{
object result = null;
if (routeData.Values.TryGetValue(name, out result))
{
return (T)result;
}
return default(T);
}
private string GetVersionFromHeader(HttpRequestMessage request)
{
if (request.Headers.Contains(VERSION_HEADER_NAME))
{
var header = request.Headers.GetValues(VERSION_HEADER_NAME).FirstOrDefault();
if (header != null)
{
return header;
}
}
return "1";
}
private string GetVersionFromQueryString(HttpRequestMessage request)
{
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
var version = query[VERSION_QUERY_NAME];
if (version != null)
{
return version;
}
return "1";
}
}