All About Attribute-Based (Direct) Routing in Web API 2

 15-Dec-2018   nityaprakash     WebAPI  Routing    Comments  0

In this post I am going to and direct aka attributes based routing. Routing is the first thing in the request which used to determine the controller and action from the request. In my previous article, I have mentioned that after HttpServer, HttpRouterDispatcher determine the which controller and action to execute, this comes determines from Route Data.

Attribute Based (Direct) Routing

Direct routing allows routes to be defined by applying attributes to action method or controller classes.

  • Route attribute is used in action method to define route. It looks like below. Value in this attribute either can be full url or partial Url.
  • full Url Route("api/today/dayofweek"), in this case url will like http://mywebsite.com/api/today/dayofweek.
  • Partial Url Route("dayofweek"). In this case we must specify route prefix at controller level

	[RoutePrefix("api/today")]
    public class TodayController : ApiController
    {
        // GET: Today
        [HttpGet]
        [Route("dayofweek")]
        public string DayOfWeek()
        {
            return DateTime.Now.ToString("dddd");
        }
		[HttpGet]
        [Route("daynumber")]
        public int DayNumber()
        {
            return DateTime.Now.Day;
        }
    }

Difining Optional Parameters

There are couple of ways specifing optional value

  1. [Route("dayofweek/{day}?")] is one way to specify but in this case parameter in the method must have default value which need to handled in code.


    
    ...
    [Route("dayofweek/{day}?")]
    public string DayOfWeek(int day = -1)
    ...
    
    
    In this case if we call url api/today/dayofweek than RequestContext.RouteData will not contain the key day and parameter "day" will be default value -1.

  2. Definging A Default Segment value: Just check below code, it set the default value in route segment only. If you don't pass day value it will automatically add "day" key in RequestContext.RouteData.


    
    ...
    [Route("dayofweek/{day=-1}")]
    public string DayOfWeek(int day)
    ...
    
    

Applying a Contraint to a Direct Route

Constraint are always applied with a colon (the : character) after the parameter name.


	...
	[Route("dayofweek/{day:int= -1}")]
	public string DayOfWeek(int day)
	...
	...
	[Route("dayofweek/{day:range(0,6)}")]
	public string DayOfWeek(int day)
	...

Ordering direct routes in controller

In convention based routing, ordering works in the way it is defined in WebApiConfig class, but it work slightly different in attribute based routing. To work out the order in which direct route in controller are applied, the URL routing feature calculates the precedence of each segment in the route template or each direct route. The precendence is a decimal value, which is then used to sort the routes so that the lowest values are match first. For each segment, a score is awarded based on the segment type.

Segment Type Score
Fixed segment 1
Variable segment with a constraint 2
Variable segment without a constraint 3
Catchall segment with a constraint 4
Catchall segment without a constraint 5

Following APIs will have precenence as per below

Route Precedence
api/today/dayofweek 1.11
api/today/dayofweek/{day:range(0,6) 1.112
getdaynumber 1.0

So order of apis will as below:

  1. /getdaynumber
  2. api/today/dayofweek
  3. api/today/dayofweek/{day:range}

Here you might have seen that there might some error arise due to same precendence for controller. In that case alphabetic order wil take prcedence and unwanted result can occure. In order to fix issue we can pass one more parameter to route Order like below.


	[Route("~/getdaynumber", Order = 1)]
	public string GetDayNumber(int day)

Define default action for a API Controller

Generally Get method without parameter is default action for the url if there is no action specified on URL. But if we want controller to execute specific method that we have to define at controller level like below:


[RoutePrefix("api/today")]
[Route("{action=DayOfWeek}")]
public class TodayController : ApiController
{
    [HttpGet]
    public string DayOfWeek()
    {
        return DateTime.Now.ToString("dddd");
    }

    // GET: Today
    [HttpGet]
    [Route("dayofweek/{day:range(1,6)}")]
    public string DayOfWeek(int day)
    {
        return Enum.GetValues(typeof(DayOfWeek)).GetValue(day).ToString();
    }
 
}

In above example I have used [Route("{action=DayOfWeek}")] attribute tell that if there is no action specified in url than execute DayOfWeek action method. We can also move route [Route("dayofweek/{day:range(1,6)}")] to at controller level. We just have to replace it with [Route("{action=dayofweek}/{day:range(1,6)}")]. This will allow to define all routes for that controller at one place like convention-based routing.

Summary

Attribute based routing is easy to implement and we don't need to define upfront in the config file. However, application have to evaluate all route and createh HttpRouteCollection in starting of the application, which might slowdown the startup process. But once it is started it remains available for finding the right Controller. We have learned, that how can we define routes with and without constraints in attributes itself.


Nitya Prakash Sharma has over 10 years of experience in .NET technology. He is currently working as Senior Consultant in industry. He is always keen to learn new things in Technology and eager to apply wherever is possible. He is also has interest in Photography, sketching and painting.

My Blog
Post Comment

COMMENTS