{"id":1688,"date":"2013-08-21T00:00:00","date_gmt":"2013-08-21T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/attribute-routing-in-web-api-v2\/"},"modified":"2021-05-17T18:34:54","modified_gmt":"2021-05-17T18:34:54","slug":"attribute-routing-in-web-api-v2","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/attribute-routing-in-web-api-v2\/","title":{"rendered":"Attribute Routing in Web API v2"},"content":{"rendered":"<div class=\"article-content\">\n<p class=\"start\">Today  we&#8217;re all used to software services exposed over HTTP. We also expect  these services to be available as plain HTTP callable endpoints: no proxies and  no special API to sit in between as a mediator. This was not always the case, as  you may recall.<\/p>\n<h2>When WCF Came on Stage<\/h2>\n<p>In the  Microsoft stack, at some point we&#8217;ve been given WCF as the framework to use to  expose externally callable endpoints. WCF was a great platform: extensible,  flexible, scalable and probably close to perfection. A few years later, though,  some people raised the point that maybe WCF was a bit over-engineered and that  what had to be made easier and faster for WCF to meet actual needs &#160;was just exposing HTTP callable  endpoints. WCF was then topped with a long list of additional frameworks and  starter kits to make programming HTTP endpoints easier. But easier coding  doesn&#8217;t necessarily mean faster code. <\/p>\n<p>ASP.NET  MVC was particularly welcome also because it features controllers that can  return JSON or XML. In a nutshell, in ASP.NET MVC you can have HTTP services at  sole cost of adding a new controller class or a specific method to an existing  controller class. This has always worked since version 1 of the framework.  Controllers are much easier to set up than a WCF service and run as fast as  reasonably possible. So WCF was quickly restricted to where the transportation  had to be TCP or MSMQ, but not so much with HTTP.<\/p>\n<h2>Then It Came Web API<\/h2>\n<p>Problem  solved? You bet! Even though ASP.NET MVC works very well, it still leaves  something to be desired. ASP.NET MVC is  tied closely to the ASP.NET framework and IIS. Web API came out to turn the good  things in ASP.NET MVC into a new framework for HTTP services. The Web API  framework relies on a different runtime environment that is totally separated  from that of ASP.NET MVC. The runtime environment is inevitably largely inspired  by ASP.NET MVC but, overall, it looks more straight-to-the-point as it is  expected to only serve data and not markup.<\/p>\n<p>Web API  is close to its version 2 release. Version 2 is an evolutionary new release that  enables and simplifies a few new features in Web API that were already available  to ASP.NET MVC web developers. An example is external authentication now  available through services such as Facebook and Google; another example is  support for Cross-Origin Resource Sharing (CORS). Yet another example, and  probably the most relevant, is attribute routing.<\/p>\n<p>From a  technical perspective, attribute routing is nothing special. Overall, it is a  powerful feature that is relatively easy to explain and understand. However, the  most interesting aspect of attribute routing in Web API is the reason why it  exists and the original ideas it is based on. Let&#8217;s just start from here.<\/p>\n<h2>Routing and URL Templates<\/h2>\n<p> Attribute routing is not a feature that Web API inherits from ASP.NET MVC. Quite  the reverse, attribute routing comes from old-fashioned WCF Web HTTP binding.  Routing has always been a constituent part of ASP.NET MVC; attribute routing,  instead, is a new variation of it.<\/p>\n<p>Classic  routing, which is still available in ASP.NET MVC 5 and Web API 2, is based on  conventions. In <b>global.asax<\/b>, at the  startup of the application, you populate a system dictionary with routes and let  the system live with all of them. Any time a request comes in, the URL is  matched against the template of registered routes. If a match is found, the  appropriate controller and action methods to serve the request are determined  from the template. If not, the request is denied and the result is usually a 404  message. The code below is taken from the default Visual Studio ASP.NET MVC  project and shows the suggested way to register routes. The <b>RegisterRoutes<\/b> method is invoked from  within <b>Application_Start<\/b> in <b>global.asax<\/b><\/p>\n<\/p>\n<pre class=\"lang:c# theme:vs2012\">public static void RegisterRoutes(RouteCollection routes)\n{\n    routes.IgnoreRoute(\"{resource}.axd\/{*pathInfo}\");\n    routes.MapRoute(\n          name: \"Default\",\n          url: \"{controller}\/{action}\/{id}\",\n          defaults: new { controller = \"Home\", action = \"Index\", id = UrlParameter.Optional }\n    );\n}<\/pre>\n<p>As a  side-note, It also makes it easier  for testing purposes to have a method prototyped just as <b>RegisterRoutes<\/b>. In a test you can call <b>RegisterRoutes<\/b> by passing a fake dictionary and then use the fake  dictionary in the assertions.<\/p>\n<p>There is  another aspect, called route recognition, that is significant for the evolution  towards attribute routing. Route recognition works on a first-match basis. The  most immediate consequence of first-match is that the order in which routes are  registered is essential. Most specific routes should appear first in the list;  catch-all and most generic routes should go to the bottom of the list.<\/p>\n<p>Why is  this so significant? Well, in large applications, or even in medium-sized  applications with a strong REST flavor, the number of routes may be quite large:  and could easily be in the order of hundreds. If you&#8217;re a REST fanatic or if  you&#8217;re just fussy about the URLs that your web application should support, then  you may quickly find that classic routing becomes a bit overwhelming to handle.  It can definitely become hard to determine the right order of more than 200  routes and you may find that infinite loops around regression are detected  during automated tests or notified by users and testers. <\/p>\n<p> Attribute routing, which is coming in ASP.NET MVC 5 and Web API 2, offers a  smoother way to handle routing. This is especially true when you want to force a  particular syntax across all pages and, more likely, all the objects you expose.  <\/p>\n<h2>The Downside of Classic  Routes<\/h2>\n<p>A route  is a plain parameterized string. You use names in curly brackets to define  parameters and concatenate numbers, static text and parameters in a sequence  that ultimately determines the URL. A route is always the same thing whether you  call it a classic ASP.NET MVC-style route or an attribute route. The difference  is all in how you use it and where you define it. <\/p>\n<p>A  positive aspect of classic routes is that they are defined in a single place and  work the same way regardless of the controller. Overall, you deal with one  simple rule: any time a request is placed that matches any of the routes it is  picked up. In addition, you can use constraints to restrict values in route  parameters. So far so good. Now consider the following common example:<\/p>\n<\/p>\n<pre class=\"lang:c# theme:vs2012\">orders\/show\/1<\/pre>\n<p>The  purpose of the URL is fairly obvious: show me the order with an ID of 1.  However, in doing so you are accepting a couple of conventions. First, you must  have a class named OrdersController. Second, and more importantly, the  OrdersController class must have a public method named Show which accepts an  integer. In addition, you must find a way to handle the case in which the  integer-the ID-is not provided but for some reasons the resulting URL is matched  to the same route.<\/p>\n<p>Among  other things, existing conventions open up to the risk of having non-action  public methods on controller class. If you happen to have a public method that  is not supposed to be callable from the outside you must explicitly opt it out  of using the NonAction attribute, otherwise it will be processed anyway, thereby  creating a potential security hole.<\/p>\n<p>There&#8217;s  a way for developers to hide the name of the method from the URL. It requires a  custom route handler, as below.<\/p>\n<pre class=\"lang:c# theme:vs2012\">public class OrdersRouteHandler : IRouteHandler\n{\n    public IHttpHandler GetHttpHandler(RequestContext requestContext)\n    {\n        \/\/ Analyzes the actual URL being mapped to the route. If so,\n        \/\/ programmatically determines controller\/action\n        if (requestContext.HttpContext.Request.Url.AbsolutePath.StartsWith(\"\/orders\/show\/\"))\n        {\n            requestContext.RouteData.Values[\"controller\"] = \"orders\";\n            requestContext.RouteData.Values[\"action\"] = \"display\";\n        }\n        return new MvcHandler(requestContext);\n    }\n}<\/pre>\n<p>Within a  custom route handler you can decide just about everything, including the real  name of controller to be used and the action method. <\/p>\n<p> Unfortunately, a custom route handler is just added to the list of routes and  you must find the right place for it. It&#8217;s no big deal as long as you have 10  routes; it starts getting a problem when you want to exercise a close control  over too many individual routes. Here&#8217;s the code you need in order to add a  route with a custom handler.<\/p>\n<pre class=\"lang:c# theme:vs2012\">var optionals = new RouteValueDictionary { \n     { \"controller\", UrlParameter.Optional }, \n     { \"action\", UrlParameter.Optional } };\nroutes.Add(\"CustomOrdersRoute\",\n            new Route(\"customordersroute\", optionals, new OrdersRouteHandler()));<\/pre>\n<p>Worse  yet, when you have a long list of routes, each of which expresses a specific URL  template, it is not unusual that you end up with many route handlers-nearly one  per route. You understand that this is not particularly satisfactory. In the  end, classic routes don&#8217;t lack any functionality; they&#8217;re simply hard to use  when you&#8217;re fussy about URL templates. This is quite common in a REST-intensive  design. Enter attribute routing.<\/p>\n<h2>Attribute Routing in  Action<\/h2>\n<p>From the  technical perspective, attribute routes are as easy as conventional routes  except that they work better in a REST design. As their name may suggest,  attribute routing is all about having a route attached, as an attribute, to a  specific action method.<\/p>\n<pre class=\"lang:c# theme:vs2012\">[WebGet(UriTemplate=\"orders\/{id}\/show\")]\nOrder GetOrderById(int id);\n<\/pre>\n<p>The code  sets the method <b>GetOrderById<\/b> to be  available over a HTTP GET call only if the URL template matches the specified  pattern. The route parameter-the <b>orderId <\/b>token-must match one of the parameters defined in the method&#8217;s signature.  There are a few more details to be discussed, but the gist of attribute routes  is all here.<\/p>\n<p>There&#8217;s  a clear resemblance with the now old-fashioned WCF Web HTTP programming model  and the <b>WebGet<\/b> attribute in  particular.<\/p>\n<pre class=\"lang:c# theme:vs2012\">[WebGet(UriTemplate=\"orders\/{id}\/show\")]Order GetOrderById(int id);<\/pre>\n<p>As I see  things, attribute routing is just the revamped and extended version of routing  as implemented in the WCF Web HTTP programming model. And, more importantly, it  fits a specific requirement that programmers currently have.<\/p>\n<h2>Enabling Attribute Routing<\/h2>\n<p>Attribute routing is not enabled by default, but it can work side-by-side with  conventional routing. Here&#8217;s the standard way to enable it:<\/p>\n<pre class=\"lang:c# theme:vs2012\">public static class WebApi2Config\n{\n    public static void Setup(HttpConfiguration config)\n    {\n        config.MapHttpAttributeRoutes();\n    }\n}<\/pre>\n<p>You  don&#8217;t have to use a <b>config<\/b> class with static methods in pure MVC4 style; what really  matters is that you invoke the new <b> MapHttpAttributeRoutes<\/b> method on the <b> HttpConfiguration<\/b> class. In case you intend to use the two types of routing  together, it is preferable you give precedence to attribute routing. This means  you call <b>MapHttpAttributeRoutes<\/b>  before you start adding global routes.<\/p>\n<p>You can define a route via attribute for each method you like and also filter on the  HTTP method used to call the action. In Web API 2, attribute classes like <b>HttpGet<\/b>,<b> HttpPost<\/b>, <b>HttpPut<\/b> and all others have been  extended with an overload that accept a route URL template. If you like it  better, you can also use the <b>AcceptVerbs<\/b>  attribute, where the first parameter indicates the method name and the second  parameter sets the route.<\/p>\n<pre class=\"lang:c# theme:vs2012\">[AcceptVerbs(\"GET\", \"orders\/{id}\/show\")]<\/pre>\n<p>You can use <b>AcceptVerbs<\/b> also for unsupported HTTP methods or perhaps WebDAV methods.<\/p>\n<h2>Attribute Route Constraints<\/h2>\n<p>Attribute routing also supports constraints on parameters using a slightly  different syntax than classic routing. The API has a list of predefined  constraints such as <b>int, bool, alpha,  min, max, length, minlength, range.<\/b> Here&#8217;s how to use them:<\/p>\n<pre class=\"lang:c# theme:vs2012\">[HttpGet(\"orders\/{id:int}\/show\"]<\/pre>\n<p>The goal  of constraints is straightforward. For more details, you can check out some  documentation at <a href=\"http:\/\/www.asp.net\/web-api\/overview\/web-api-routing-and-actions\/attribute-routing-in-web-api-2\"> http:\/\/www.asp.net\/web-api\/overview\/web-api-routing-and-actions\/attribute-routing-in-web-api-2<\/a>.  <\/p>\n<p>You can concatenate as many constraints as you wish and can create custom constraints as well. <\/p>\n<pre class=\"lang:c# theme:vs2012\">[HttpGet(\"orders\/{id:int:range(1, 100)}\/show\"]<\/pre>\n<p>To  create a custom constraint you create a class that implements the<b> IHttpRouteConstraint<\/b> interface and use the following code to  register it:<\/p>\n<pre class=\"lang:c# theme:vs2012\">var resolver = new DefaultInlineConstraintResolver();\nresolver.ConstraintMap.Add(\"custom\", typeof(YourCustomConstraint));\nconfig.MapHttpAttributeRoutes(new HttpRouteBuilder(resolver));<\/pre>\n<p>The  interface <b>IHttpRouteConstraint<\/b> has a  single method named Match. Finally, you should note that each controller class  can be decorated with a route prefix string in order to minimize the route  string. When a route prefix is defined it is appended to any attribute routes  before parsing. Here&#8217;s an example:<\/p>\n<pre class=\"lang:c# theme:vs2012\">[RoutePrefix(\"orders\")]\npublic class OrdersController : ApiController\n{\n    [HttpGet(\"{id:int:range(1, 100)}\/show }\")]  \n    public Order GetOrderById(int id) \n    { \n      :\n    }\n    :\n}<\/pre>\n<p>You can  have multiple route prefixes for a controller; when this happens each route is  evaluated individually in the order it appears until a match is found.<\/p>\n<h2>Summary<\/h2>\n<p>At every new build of ASP.NET MVC 5 and Web API  2, attribute routing gains in importance. It is likely to also end up making a  debut in plain ASP.NET MVC. Attribute routing is not the type of feature that  alone will push you to use Web API. However, it&#8217;s just one of those features  that&#8217;s really nice to have.<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Attribute routing solves a lot of the problems of classic ASP.NET routing, which can get ungainly when there are many  route handlers, or you have several  REST services. Dino shows how to enable and use attribute routing  in ASP.NET MVC&hellip;<\/p>\n","protected":false},"author":221911,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4156,4157,5885,5166,5456,5886,5887],"coauthors":[],"class_list":["post-1688","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-asp","tag-asp-net","tag-attributes","tag-mvc","tag-routing","tag-web-api","tag-wfc"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1688","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221911"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1688"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1688\/revisions"}],"predecessor-version":[{"id":40946,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1688\/revisions\/40946"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1688"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1688"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1688"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1688"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}