{"id":1111,"date":"2017-05-03T20:50:32","date_gmt":"2017-05-03T20:50:32","guid":{"rendered":"http:\/\/tpodolak.com\/blog\/?p=1111"},"modified":"2017-05-08T08:17:08","modified_gmt":"2017-05-08T08:17:08","slug":"asp-net-core-default-api-version-with-url-path-versioning","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2017\/05\/03\/asp-net-core-default-api-version-with-url-path-versioning\/","title":{"rendered":"ASP.NET Core &#8211; default API version with url path versioning"},"content":{"rendered":"<h3>1. Introduction.<\/h3>\n<p>When you start versioning your <i>API<\/i> sometimes you want to have access to your endpoints without specifying version explicitly. Thanks to <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNetCore.Mvc.Versioning\"> Microsoft.AspNetCore.Mvc.Versioning<\/a> library this can be easily achieved with proper setup of configuration action. However, it turns out that in order to enable default <i>API<\/i> version for url path versioning, there are a couple additional steps to do.<\/p>\n<h3>2. Default <i>API<\/i> version without url path versioning<\/h3>\n<p>Let&#8217;s start with the initial setup of <i>API<\/i> versioning which will work for every type of versioning apart from url path versioning. All you have to do is to assign an <i>ApiVersionSelector<\/i> and set the <i>AssumeDefaultVersionWhenUnspecified<\/i> property to true.<\/p>\n<pre lang=\"csharp\">\r\npublic void ConfigureServices(IServiceCollection services)\r\n{\r\n    \/\/ Add framework services.\r\n    services.AddApiVersioning(options =>\r\n    {\r\n        options.ApiVersionReader = new QueryStringApiVersionReader();\r\n        options.AssumeDefaultVersionWhenUnspecified = true;\r\n        options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);\r\n    });\r\n}\r\n<\/pre>\n<p>Now when the user will try to access the endpoint without specifying version explicitly the <i>ApiVersionSelector<\/i> will select proper version &#8211; in this case highest available version. <\/p>\n<h3>3. Default Api version with URL path versioning<\/h3>\n<p>Running the same configuration against controllers which are versioned via url path (not query string)<\/p>\n<pre lang=\"csharp\">\r\nnamespace AspNetCoreDefaultApiVersionAndRoutePrefix.Controllers.V1\r\n{\r\n    [Route(\"v{version:apiVersion}\/[controller]\")]\r\n    [ApiVersion(\"1\")]\r\n    public class ValuesController : Controller\r\n    {\r\n        \/\/ GET api\/values\r\n        [HttpGet]\r\n        public IEnumerable<string> Get()\r\n        {\r\n            return new string[] {\"V1\"};\r\n        }\r\n    }\r\n}\r\n\r\nnamespace AspNetCoreDefaultApiVersionAndRoutePrefix.Controllers.V2\r\n{\r\n    [Route(\"v{version:apiVersion}\/[controller]\")]\r\n    [ApiVersion(\"2\")]\r\n    public class ValuesController : Controller\r\n    {\r\n        \/\/ GET api\/values\r\n        [HttpGet]\r\n        public IEnumerable<string> Get()\r\n        {\r\n            return new string[] {\"V2\"};\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>will result with <i>404<\/i> status code, as routing is not able to match the url.<br \/>\n<a href=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/404.png\" rel=\"attachment wp-att-1127\"><img decoding=\"async\" src=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/404.png\" alt=\"404\" height=\"461\" class=\"aligncenter size-full wp-image-1127\" srcset=\"https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/404.png 935w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/404-150x74.png 150w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/404-300x148.png 300w\" sizes=\"(max-width: 935px) 100vw, 935px\" \/><\/a><br \/>\nFortunately, unlike the <i>ASP.NET Web API<\/i>, <i>ASP.NET Core<\/i> allows us to have multiple route attributes applied to given controller, so in order to fix the issue we just have to add <\/p>\n<pre lang=\"csharp\">\r\n[Route(\"[controller]\")]\r\n<\/pre>\n<p>attribute to versioned controllers. From now on we can access our <i>API<\/i> without specifying version as well as with version specified.<br \/>\n<a href=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/after.png\" rel=\"attachment wp-att-1128\"><img loading=\"lazy\" decoding=\"async\" src=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/after.png\" alt=\"after\" width=\"1046\" height=\"177\" class=\"aligncenter size-full wp-image-1128\" srcset=\"https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/after.png 1046w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/after-150x25.png 150w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/after-300x51.png 300w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2017\/05\/asp-net-core-default-api-version-with-url-path-versioning\/after-1024x173.png 1024w\" sizes=\"auto, (max-width: 1046px) 100vw, 1046px\" \/><\/a><\/p>\n<h3>4. Adding default route with convention<\/h3>\n<p>If you don\u2019t want to specify default route manually for every controller, you can do it via application model convention. The interesting thing about it is the fact that apart from changing existing values of application model we can also add new items to it. This means that we can add additional <i>SelectorModel<\/i> with our default route to <i>Selectors&#8217;<\/i> collection<\/p>\n<pre lang=\"csharp\">\r\npublic class DefaultRoutePrefixConvention : IApplicationModelConvention\r\n{\r\n    public void Apply(ApplicationModel application)\r\n    {\r\n        foreach (var applicationController in application.Controllers)\r\n        {\r\n            applicationController.Selectors.Add(new SelectorModel\r\n            {\r\n                AttributeRouteModel = new AttributeRouteModel\r\n                {\r\n                    Template = \"[controller]\"\r\n                }\r\n            });\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Now we can get rid of <i>[Route(&#8220;[controller]&#8221;)]<\/i> attribute from our versioned controllers and just rely on convention which can be added as follows<\/p>\n<pre lang=\"csharp\">\r\npublic void ConfigureServices(IServiceCollection services)\r\n{\r\n    \/\/ rest of the code omitted for brevity \r\n    services.AddMvc(mvc =>\r\n    {\r\n        mvc.Conventions.Add(new DefaultRoutePrefixConvention());\r\n    });\r\n}\r\n<\/pre>\n<p>Source code for this post can be found <a href=\"https:\/\/github.com\/tpodolak\/Blog\/tree\/master\/AspNetCoreDefaultApiVersionWithUrlPathVersioning\">here<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Introduction. When you start versioning your API sometimes you want to have access to your endpoints without specifying version explicitly. Thanks to Microsoft.AspNetCore.Mvc.Versioning library this can be easily achieved with proper setup of configuration action. However, it turns out that in order to enable default API version for url path versioning, there are a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[317],"tags":[318,323],"class_list":["post-1111","post","type-post","status-publish","format-standard","hentry","category-asp-net-core","tag-asp-net-core","tag-versioning"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1111","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/comments?post=1111"}],"version-history":[{"count":10,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1111\/revisions"}],"predecessor-version":[{"id":1136,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1111\/revisions\/1136"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=1111"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=1111"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=1111"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}