Routing in ASP NET Web API - For different versions of an API
Solution 1
There are many ways to implement versionning with attribute routing ; A really basic way is to use RoutePrefix attribute for each version of your ApiController
[RoutePrefix("v1")]
public class V1_ProductsController : ApiController
{
[Route("products")]
public IEnumerable<string> Get()
{
return new string[] { "v1-product1", "v1-product2" };
}
//....
}
[RoutePrefix("v2")]
public class V2_ProductsController : ApiController
{
[Route("products")]
public IEnumerable<string> Get()
{
return new string[] { "v2-product1", "v2-product2" };
}
//....
}
/v1/products
goes to the first version of /v2/products
goes to the second one.
Solution 2
You can do it by overriding DefaultHttpControllerSelector
there you override method to selectcontroller
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
HttpControllerDescriptor controllerDescriptor = null;
IDictionary<string, HttpControllerDescriptor> controllers = GetControllerMapping();
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
object apiVersion;
if (!routeData.Values.TryGetValue("Version", out apiVersion))
{
apiVersion = "1";
}
object controllerName;
if (!routeData.Values.TryGetValue("controller", out controllerName))
{
controllerName = string.Empty;
}
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
string newControllerName = String.Concat(controllerName.ToString(), "V", apiVersion);
if (controllers.TryGetValue(newControllerName, out controllerDescriptor))
{
return controllerDescriptor;
}
if (controllers.TryGetValue(controllerName.ToString(), out controllerDescriptor))
{
return controllerDescriptor;
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
Then you are adding routes webapiconfig
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and register controller selector in webapiconfig
config.Services.Replace(typeof(IHttpControllerSelector), new ApiVersioningSelector(config));
So from now if you name controller ProductsV1Controller it will reffer /api/v1/products. Also please note that my example also support routes without version so if v1 is not found it will try to check if ProductsController exists
PS. Code is update one bug was there :(
Solution 3
Another simple way is configuring your route as api/{folder}/{controller}/{action} where in to folder you can give name as V1 or V2.
A good way can be implementing your own Controller selector. You can use this link for more information.
The interface that Web API uses to select a controller is IHttpControllerSelector
. The important method on this interface is SelectController, which selects a controller for a given HttpRequestMessage.
now he who must not be named.
#SOreadytohelp There is this joy in helping others. Thanks SO
Updated on June 26, 2022Comments
-
now he who must not be named. almost 2 years
I am reading about
Attribute Routing in Web API 2
from hereThe article says,
Here are some other patterns that attribute routing makes easy. API versioning In this example, “/api/v1/products” would be routed to a different controller than “/api/v2/products”. /api/v1/products /api/v2/products
How come?
EDIT: I would do this in Normal Routing:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/v2/products", defaults: new { controller = V2_Products } ); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/v1/products", defaults: new { controller = V1_Products } ); } }
Could anyone explain me how to do this in
Attribute Routing
way ? And how come usingAttribute routing
is easier and convenient for this example (according to the article) ? -
user3140169 over 8 yearsVolodymyr Bilyachat answer's bug is this line string newControllerName = String.Concat(controllerName.ToString(), "V", apiVersion); fix: string newControllerName = String.Format("Controllers.{0}{1}", controllerName.ToString(), apiVersion);
-
DSA about 8 yearsI have an issue with this method when i define custom route for each api metho. i.e. I would like to define Route for my method [Route("Product")]. Any idea on how can we define custom route along with this overriding?
-
Vova Bilyachat about 8 yearsSo what you want to do if route is there?
-
DSA about 8 yearsLet's say i have two diff post method with same parameter, i have to define custom route for each. But if define route, this overriding stops working. It says, method not allowed.
-
Vova Bilyachat about 8 yearsYou need to check if action has route then use base class to handle that
-
DSA about 8 yearsdo you mean we need to hard code route on base class?
-
Vova Bilyachat about 8 yearsNo i mean that what you can do its check if your Action method has attribute route and then use base class from HttpControllerSelector to return