{"id":1754,"date":"2013-03-07T21:46:00","date_gmt":"2013-03-07T21:46:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/2013\/03\/07\/asp-net-web-api-using-namespaces-to-version-web-apis\/"},"modified":"2013-03-07T21:46:00","modified_gmt":"2013-03-07T21:46:00","slug":"asp-net-web-api-using-namespaces-to-version-web-apis","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-web-api-using-namespaces-to-version-web-apis\/","title":{"rendered":"ASP.NET Web API: Using Namespaces to Version Web APIs"},"content":{"rendered":"<p>In this post, I&rsquo;ll show how to extend the routing logic in ASP.NET Web API, by creating a custom controller selector. Suppose that you want to version your web API by defining URIs like the following:<\/p>\n<p style=\"padding-left: 30px\">\/api\/v1\/products\/<\/p>\n<p style=\"padding-left: 30px\">\/api\/v2\/products\/<\/p>\n<p>You might try to make this work by creating two different &ldquo;Products&rdquo; controllers, and placing them in separate namespaces:<\/p>\n<pre>namespace MyApp.Controllers.V1\n{\n    \/\/ Version 1 controller\n    public class ProductsController : ApiController {  }\n}\n\nnamespace MyApp.Controllers.V2\n{\n    \/\/ Version 2 controller\n    public class ProductsController : ApiController {  }\n}\n<\/pre>\n<p>The problem with this approach is that Web API finds controllers by class name, ignoring the namespace. So there&rsquo;s no way to make this work using the default routing logic. Fortunately, Web API makes it easy to change the default behavior.<\/p>\n<p>The interface that Web API uses to select a controller is <strong>IHttpControllerSelector<\/strong>. You can read about the default implementation <a href=\"http:\/\/www.asp.net\/web-api\/overview\/web-api-routing-and-actions\/routing-and-action-selection\">here<\/a>. The important method on this interface is <strong>SelectController<\/strong>, which selects a controller for a given <strong>HttpRequestMessage<\/strong>.<\/p>\n<p>First, you need to understand a little about the Web API routing process. Routing starts with a route template. When you create a Web API project, it adds a default route template:<\/p>\n<p style=\"padding-left: 30px\">&#8220;api\/{controller}\/{id}&#8221;<\/p>\n<p>The parts in curly brackets are placeholders. Here is a URI that matches this template:<\/p>\n<p style=\"padding-left: 30px\">http:\/\/www.example.com\/api\/products\/1<\/p>\n<p>So in this example, the placeholders have these values:<\/p>\n<ul>\n<li>controller = products<\/li>\n<li>id = 1<\/li>\n<\/ul>\n<p>The default <strong>IHttpControllerSelector<\/strong> uses the value of &ldquo;controller&rdquo; to find a controller with a matching name. In this example, &ldquo;products&rdquo; would match a controller class named ProductsController. (By convention, you need to add the &ldquo;Controller&rdquo; suffix to the class name.)<\/p>\n<p>To make our namespace scenario work, we&rsquo;ll use a route template like this:<\/p>\n<p style=\"padding-left: 30px\">&#8220;api\/{namespace}\/{controller}\/{id}&#8221;<\/p>\n<p>Here is a matching URI:<\/p>\n<p style=\"padding-left: 30px\">http:\/\/www.example.com\/api\/v1\/products\/1<\/p>\n<p>And here are the placeholder values:<\/p>\n<ul>\n<li>namespace = v1<\/li>\n<li>controller = products<\/li>\n<li>id = 1<\/li>\n<\/ul>\n<p>Now we can use these values to find a matching controller. First, call <strong>GetRouteData<\/strong> to get an <strong>IHttpRouteData<\/strong> object from the request:<\/p>\n<pre>public HttpControllerDescriptor SelectController(HttpRequestMessage request)\n{\n    IHttpRouteData routeData = request.GetRouteData();\n    if (routeData == null)\n    {\n        throw new HttpResponseException(HttpStatusCode.NotFound);\n    }\n\n    \/\/ ...\n<\/pre>\n<p>Use <strong>IHttpRouteData<\/strong> to look up the values of &ldquo;namespace&rdquo; and &ldquo;controller&rdquo;. The values are stored in a dictionary as <strong>object<\/strong> types. Here is a helper method that returns a route value as a type <em>T<\/em>:<\/p>\n<pre>private static T GetRouteVariable(IHttpRouteData routeData, string name)\n{\n    object result = null;\n    if (routeData.Values.TryGetValue(name, out result))\n    {\n        return (T)result;\n    }\n    return default(T);\n}\n<\/pre>\n<p>Use this helper function to get the route values as strings:<\/p>\n<pre>string namespaceName = GetRouteVariable&lt;string&gt;(routeData, \"namespace\");\nif (namespaceName == null)\n{\n    throw new HttpResponseException(HttpStatusCode.NotFound);\n}\n\nstring controllerName = GetRouteVariable&lt;string&gt;(routeData, \"controller\");\nif (controllerName == null)\n{\n    throw new HttpResponseException(HttpStatusCode.NotFound);\n}\n<\/pre>\n<p>Now look for a matching controller type. For example, given &ldquo;namespace&rdquo; = &ldquo;v1&rdquo; and &ldquo;controller&rdquo; = &ldquo;products&rdquo;, this would match a controller class with the fully qualified name <code>MyApp.Controllers.V1.ProductsController<\/code>.<\/p>\n<p>To get the list of controller types in the application, use the <strong>IHttpControllerTypeResolver<\/strong> interface:<\/p>\n<pre>IAssembliesResolver assembliesResolver = \n    _configuration.Services.GetAssembliesResolver();\nIHttpControllerTypeResolver controllersResolver = \n    _configuration.Services.GetHttpControllerTypeResolver();\n\nICollection&lt;Type&gt; controllerTypes = \n    controllersResolver.GetControllerTypes(assembliesResolver);\n<\/pre>\n<p>This code performs reflection on all of the assemblies in the app domain. To avoid doing this on every request, it&rsquo;s a good idea to cache a dictionary of controller types, and use the dictionary for subsequent look ups.<\/p>\n<p>The last step is to replace the default <strong>IHttpControllerSelector<\/strong> with our custom implementation, in the <strong>HttpConfiguration.Services<\/strong> collection:<\/p>\n<pre>    config.Services.Replace(typeof(IHttpControllerSelector), \n        new NamespaceHttpControllerSelector(config));\n<\/pre>\n<p>You can find the complete sample hosted on <a href=\"http:\/\/aspnet.codeplex.com\/SourceControl\/changeset\/view\/dd207952fa86#Samples\/WebApi\/NamespaceControllerSelector\/ReadMe.txt\">aspnet.codeplex.com<\/a>.<\/p>\n<p>In order to keep the code as simple as possible, the sample has a few limitations:<\/p>\n<ul>\n<li>It expects the route to contain a &ldquo;namespace&rdquo; variable. Otherwise, it returns an HTTP 404 error. You could modify the sample so that it falls back to the default <strong>IHttpControllerSector<\/strong> in this case.<\/li>\n<li>The sample always matches the value of &ldquo;namespace&rdquo; against the final segment of the namespace (i.e., the inner scope). So &ldquo;v1&rdquo; matches &ldquo;MyApp.Controllers.V1&rdquo; but not &ldquo;MyApp.V1.Controllers&rdquo;. You could change this behavior by modifying the code that constructs the dictionary of controller types. (See the <code>InitializeControllerDictionary<\/code> method.)<\/li>\n<\/ul>\n<p>Also, versioning by URI is not the only way to version a web API. See Howard Dierking&rsquo;s <a href=\"http:\/\/codebetter.com\/howarddierking\/2012\/11\/09\/versioning-restful-services\/\">blog post<\/a> for more thoughts on this topic.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, I&rsquo;ll show how to extend the routing logic in ASP.NET Web API, by creating a custom controller selector. Suppose that you want to version your web API by defining URIs like the following: \/api\/v1\/products\/ \/api\/v2\/products\/ You might try to make this work by creating two different &ldquo;Products&rdquo; controllers, and placing them in [&hellip;]<\/p>\n","protected":false},"author":444,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197],"tags":[34,7432],"class_list":["post-1754","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","tag-asp-net-web-api","tag-webapi"],"acf":[],"blog_post_summary":"<p>In this post, I&rsquo;ll show how to extend the routing logic in ASP.NET Web API, by creating a custom controller selector. Suppose that you want to version your web API by defining URIs like the following: \/api\/v1\/products\/ \/api\/v2\/products\/ You might try to make this work by creating two different &ldquo;Products&rdquo; controllers, and placing them in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/1754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/444"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=1754"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/1754\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=1754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=1754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=1754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}