Click here to Skip to main content
12,698,913 members (23,168 online)
Click here to Skip to main content
Add your own
alternative version

Stats

112.5K views
42 bookmarked
Posted

[Attribute] Routing in ASP.NET MVC 5 / WebAPI 2

, 18 May 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
[Attribute] Routing in ASP.NET MVC 5 / WebAPI 2

Introduction

In the last post, I have discussed how the routing framework actually works. In this post, I will discuss about one of the coolest latest features offered by MVC5, WebAPI2 and it's called “Attribute Routing”. Under the hood, attribute routing still maintains the same mechanism of routing framework.

So, right now, you might be thinking if new routing feature (attribute routing) also uses the same routing mechanism under the hood then where the twist actually came in the game? Well, over the last few years while developing large enterprise web apps, it was found that as the project gets bigger and special cases accumulate, it becomes hard to keep track of all those routes in a single file. Things get a bit messy while developers have to write code to apply complex constraint. In most cases, they use a custom constraint by implementing IRouteConstraint and defining the custom logic in the Match method – if it returns true, the route is a match.

public interface IRouteConstraint
{
    bool Match(HttpContextBase httpContext, Route route, 
    string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
} 

The problem with the Convention-based Routing is that since the routes are physically separated from the controllers they apply to, it often take some detective work to understand the relationships. With the hope to give a better development experience of this issue, #Microsoft has adopted “attribute routing” feature in ASP.NET MVC5 and WebApi2 from Tim McCall.

As the name implies, Attribute routing uses attributes to define routes and it can be used on controller actions and even controller classes as well. In short, I will explain how these features makes life easier. But before that, you have to enable this new feature in your solution. So, let's do it first.

Enabling Attribute Routing

To enable Attribute Routing, we need to call the MapMvcAttributeRoutes method of the route collection class during configuration.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
        //Add the following line of code
        routes.MapMvcAttributeRoutes(); 
 
       //[Code Excerpt]
    }
}

As stated earlier, you can keep both the “Convention-based Routing” as well as the “[attribute] routing” under the same web app, if so then keep the following note in mind.

MapMvcAttributeRoutes() have to call before the Convention-based Routing.

Now, once we have enabled the attribute routing, let's check out some examples.

Example

Defining a Route

A route attribute has to be defined on top of an action method or on the top of a controller. In the following example, I have declared the attribute routing on top of action method named “About()”.

public class HomeController : Controller
{
       [Route("Users/about")]
       public ActionResult About()
       {
           ViewBag.Message = "You successfully reached USERS/About route";
           return View();
       }
}

Defining a Common Prefix

If you want to specify a common prefix for an entire controller, then instead of specifying [RoutePrefix] attribute on the top of each and every action, you should specify it in the controller level.

[RoutePrefix("Movie")]
public class HomeController : Controller
{
      //Route: Movie/Index
      public ActionResult Index()
      {
           ViewBag.Message = "You are in Home Index";
           return View();
      }
 
      //Route: Movie/About
       public ActionResult About()
       {
           ViewBag.Message = "You successfully reached USERS/About route";
           return View();
       }
} 

Overriding a Common Prefix

There could be situations where you might not want your actions to be response under the same route prefix. In such cases, you have to override the common route prefix. Doing so is easy.

Use a tilde (~) on the method attribute to override the route prefix.

[RoutePrefix("Movie")]
public class HomeController : Controller
{
      //Route: Movie/Index
      [Route]
      public ActionResult Index()
      {
           ViewBag.Message = "You are in Home Index";
           return View();
      }
 
      //Route: NewRoute/About
       [Route("~/NewRoute/About")]
       public ActionResult About()
       {
           ViewBag.Message = "You successfully reached NEWRoute/About route";
           return View();
       }
}

Defining a Default Route

Now you know how to define a common prefix and how to override it in need. Our next target is to define a default route using attribute routing. First apply the [Route] attribute on the Controller level and then specify a default action as a parameter.

In the following code snippet, if not specified anything the routing framework will take the user to “/Movie/index” route.

[RoutePrefix("Movie")]
[Route("{action=index}")]
public class HomeController : Controller
{
      public ActionResult Index()
      {
           ViewBag.Message = "You are in Home Index";
           return View();
      }
 
      //Route: Movie/About
       public ActionResult About()
       {
           ViewBag.Message = "You successfully reached USERS/About route";
           return View();
       }
} 

Note: To override the default route, just specify the specific route on specific action !

Optional URI Parameter

To make any parameter optional, add a question mark to the Route parameter.

public class HomeController : Controller
{
      [Route("home/{id?}")]
      public ActionResult Employee(int id)
      {
           ViewBag.Message = "You are in Home Index";
           return View();
      }
}

Default Value in URI Parameter

To specify any parameter as default, initialize a value in the route parameter.

public class HomeController : Controller
{
      [Route("home/{type=en}")]
      public ActionResult Search(string type)
      {
           ViewBag.Message = "You are in Home Index";
           return View();
      }
}

Imposing Constraints

Imposing constraints restrict how the parameters in the route template are matched. On the top, I have shown you how to search certain employee by their id. Now, let's say we also want the end user to search employee by Name. Also assume different view has be render in that case.

home/3 <–render “View A”

home/shahriar <–render “View B”

What we can do is to add another ActionResult which receives a string parameter. But only doing so does not solve our problem. We have to explicitly define and impose certain constraints to let the routing framework work perfectly.

Notice the route carefully. In both cases, routing framework hits the home controller and then based on the action parameter received it renders different view. If we just don’t explicitly tell the routing framework to impose certain constraints, it just can’t fly you(the viewer) to the appropriate action.

The general syntax for imposing certain constraint is {parameter:constraint}.

public class HomeController : Controller
{
      [Route("home/{id? : int}")]
      public ActionResult Employee(int id)
      {
           //Logic goes here
      }
 
      [Route("home/{name}")]
      public ActionResult Employee(string name)
      {
           //Logic goes here
      }
} 

Here, the first route will only be selected if the “id” segment of the URI is an integer. Otherwise, the second route will be chosen. (“?”) symbol specifies that this parameter is Optional.

Ken Egozi posted a nice article regarding Attribute routing and there he posted a complete list of constraints with some examples that I have added below:

ConstraintDescriptionExample
alphaMatches uppercase or lowercase Latin alphabet characters (a-z, A-Z){x:alpha}
boolMatches a Boolean value.{x:bool}
datetimeMatches a DateTime value.{x:datetime}
decimalMatches a decimal value.{x:decimal}
doubleMatches a 64-bit floating-point value.{x:double}
floatMatches a 32-bit floating-point value.{x:float}
guidMatches a GUID value.{x:guid}
intMatches a 32-bit integer value.{x:int}
lengthMatches a string with the specified length or within a specified range of lengths.{x:length(6)}
{x:length(1,20)}
longMatches a 64-bit integer value.{x:long}
maxMatches an integer with a maximum value.{x:max(10)}
maxlengthMatches a string with a maximum length.{x:maxlength(10)}
minMatches an integer with a minimum value.{x:min(10)}
minlengthMatches a string with a minimum length.{x:minlength(10)}
rangeMatches an integer within a range of values.{x:range(10,50)}
regexMatches a regular expression.{x:regex(^\d{3}-\d{3}-\d{4}$)}

Pay Close Attention

  1. Some of the constraints, such as “min”, “maxlength”, “minlength” take arguments in parentheses.
  2. To apply multiple constraints to a parameter, separated each constraint by a colon.

Rrestricting specific actions to specific HTTP verbs:

Existing attributes (e.g. [HttpGet], [HttpPost] and [AcceptVerbs]) are allowed:)

Conclusion

And this brings us to end of this topic. I have tried to cover up almost all the relevant major things regarding attribute routing but yet there are some mini tips and tricks that haven't been discussed here. I leave those tiny things for you. Hope you could figure out those tiny things by yourself. Happy coding! :)

Additional Resources

  1. Maarten Balliauw’s blog
  2. Attribute routing in Web api 2
  3. http://benfoster.io/blog/improving-aspnet-mvc-routing-configuration

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author


You may also be interested in...

Comments and Discussions

 
SuggestionMessage Automatically Removed Pin
17-Nov-15 23:48
memberChristopher Andrews17-Nov-15 23:48 
GeneralMy vote of 5 Pin
Sk. Tajbir4-Aug-14 11:42
memberSk. Tajbir4-Aug-14 11:42 
GeneralMy vote of 5 Pin
Monjurul Habib21-May-14 4:46
professionalMonjurul Habib21-May-14 4:46 
GeneralRe: My vote of 5 Pin
Shahriar_cse21-May-14 9:22
memberShahriar_cse21-May-14 9:22 
QuestionVote of 5 Pin
GanesanSenthilvel19-May-14 8:49
memberGanesanSenthilvel19-May-14 8:49 
AnswerRe: Vote of 5 Pin
Shahriar_cse20-May-14 3:45
memberShahriar_cse20-May-14 3:45 
GeneralMy vote of 5 Pin
Lilanga18-May-14 22:56
professionalLilanga18-May-14 22:56 
GeneralRe: My vote of 5 Pin
Shahriar_cse19-May-14 5:31
memberShahriar_cse19-May-14 5:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170118.1 | Last Updated 19 May 2014
Article Copyright 2014 by Shahriar Hossain
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid