New type of URI Routing in MVC 5 – Attribute Routing

This article walks you through the new method of URI Routing introduced in ASP.NET MVC 5 called Attribute Routing.

What You’ll learn:

  • Why Attribute Routing?
  • How to enable it?
  • Various available options to work with it.

We have seen what URL Routing is and how it works with ASP.NET MVC in my previous Article – Understanding ASP.NET MVC Routing Mechanism.

In brief, URL Routing means how ASP.NET MVC matches the URL Pattern to an action method in a controller.

Introduction

ASP.NET introduces a new type of Routing called Attribute RoutingAs the name implies, this method uses Attributes to define the route patterns. The Routing mechanism which we used to work with in previous versions of MVC is still supported. We can use any of the ways to implement Routing. In fact, we can combine both the ways in a single application itself.

Let’s understand this attribute routing by first analyzing how we could do mapping URI pattern of an eCommerce application to an action method using Conventional way of Routing which we discussed here. Then we will see how we can accomplish the same using attribute routing.

Say, we have the following Route defined in Global.asax / RouteConfig.cs


routes.MapRoute(
   name: "SingleProductDetails",
   url: "prodID}/{prodTitle}",
   defaults: new { controller = "Products", action = "Details" },
   constraints: new { prodId = "\\d+" }
);

Here, this route definition will help MVC framework to map the requests which matches this pattern to Details action method under ProductsController and as we have defined constraint with this pattern, it will map to the below action method only if the prodId from URI can be parsed to integer.

public ActionResult Display(int prodId)        
{            
     return View();        
}

Here, we have the action method in our source code and the route definition the RouteConfig.cs file. So let’s think how it will be if we have these two parts in a single place instead of two different files. Yes, what you’re thinking is right. Attribute Routing will define Route Patterns along with the action method itself instead of defining it in other file. it can be done by using Attributes to the Action methods and Controllers.

Using Attributes, we can define the route pattern just for that particular action method as shown here

[Route("prodId}/{prodTitle}")]        
public ActionResult Display(int prodId)        
{            
     return View();        
}

Now, this provides us answer to our question – why attribute routing?

Let’s see how we can enable this attribute routing and play with all the options available.

Enable Attribute Routing

We have to enable Attribute Routing before we use “Route” Attribute with any of our Action methods.

  • First make sure that you have installed MVC 5 with any version of Visual Studio 2012 or 2013.
    • you can do it using nuget package manager console :
      • PM> Install-Package Microsoft.AspNet.Mvc
      • check this reference for help – https://www.nuget.org/packages/Microsoft.AspNet.Mvc/5.1.1
  • After installing MVC 5 or later version, you can enable Attribute Routing by calling MapMvcAttributeRoutes() method of RouteCollection in RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)        
{            
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

        //Attribute Routing            
        routes.MapMvcAttributeRoutes(); 

        //Conventional Routing            
        routes.MapRoute(                
           name: "Default",                
           url: "controller}/{action}/{id}",                
           defaults: new { controller = "Home", action = "Index", 
                           id = UrlParameter.Optional }            
        );        
}

Here in this code snippet you can observe that we have merged both conventional and attribute routing.

  • After enabling it in RouteConfig.cs We’re ready to use Route Attributes with our Action methods. Let’s do it.

Various Options to set

  • Optional parameters

Sometime you may have URI parameters as optional. See in the route pattern we defined named SingleProductDetails. In this route, the prodTitle parameter can be optional because we need only prodId for that action method. so let’s make prodTitle as optional.

  • We can make any parameter as optional by appending question mark(?) to the parameter.
[Route("{prodId}/{prodTitle?}")]

Now the URI requests could be like

both these URIs are valid for the Display action method.

  • Default parameters

Sometime you may have to pass default values to action methods if user doesn’t pass any value in URI request.

[Route("employees/{empId=0}")]        
public ActionResult DisplayEmployees(int empId)        
{            
     if(empId != 0)            
     {                
         return View("SingleEmployee", GetEmployees(empId));            
     }
     return View("AllEmployees", GetAllEmployees());        
}

here, both http://www.oursite.com/employees/10 and http://www.oursite.com/employees/ will route to this action method but the former will display only the employee with id 10 and the later will display all the employees.

  • Route Prefix

Most of the times, we will be having same prefix for all the action methods of a single controller.

public class EmployeeController : Controller    
{        
    // GET: /employees/index        
   [Route("employees")]        
   public ActionResult Index()        
   {            
        return View();        
   }    

   // GET: /employees/5        
   [Route("employees/{empId}")]        
   public ActionResult Details(int id)        
   {            
       return View();        
   }          

   // GET: /employees/5/edit        
   [Route("employees/{empId}/edit")]        
   public ActionResult Edit(int id)        
   {            
        return View();        
   } 

   // GET: /employees/5/delete        
   [Route("employees/{empId}/delete")]        
   public ActionResult Delete(int id)        
   {            
        return View();        
   } 
}

If you observer route patterns for the above action methods, its starts with “employees” for all the action methods. In these cases, we can can RoutePrefix attribute for the controller itself so that we can avoid writing it for each and every action in it.

So it should look like this after having RoutePrefix for the controller.

[RoutePrefix("employees")]    
public class EmployeeController : Controller    
{        
    // GET: /employees/        
    [Route]        
    public ActionResult Index()        
    {            
         return View();        
    }         

    // GET: /employees/5        
    [Route("empId}")]        
    public ActionResult Details(int id)        
    {            
        return View();        
    }                        

    // GET: /employees/5/edit        
    [Route("{empId}/edit")]        
    public ActionResult Edit(int id)        
    {   
         return View();        
    }

    // GET: /employees/5/delete        
    [Route("{empId}/delete")]        
    public ActionResult Delete(int id)          
    {            
         return View();        
    }
}

If you want to override the route pattern for any particular action in that controller, you can use tild (~)  operator to override the route prefix defined for that controller.

It should look like this:

[RoutePrefix("employees")]    
public class EmployeeController : Controller    
{        
    // GET: /employees/5        
    [Route("{empId}")] 
    public ActionResult Details(int id)        
    {            
       return View();        
    }      
    
    // GET: /emp/5/delete        
    [Route("~/emp/{empId}/delete")]        
    public ActionResult Delete(int id)        
    {   
         return View();        
    } 
}
  • Route Attribute on Controller

We can even set this Route attribute on the Controller itself, wherein it will accept the Action as the parameter. So, it will create the default route patterns for all the action methods inside the controller.

[RoutePrefix("employees")]    
[Route("{action=index}")]    
public class EmployeeController : Controller    
{        
     // GET: /employees/        
     public ActionResult Index()        
     {
            return View();        
     } 

     // GET: /employees/list       
     public ActionResult List()        
     {   
         return View();        
     }
         
     // GET: /employees/create        
     public ActionResult Create()        
     {
            return View();        
     }
}
  • Applying Constraints on Routes

If we want to apply any constraint on the route parameters, we can do it by placing colon “:” and then the constraint.

// GET: /employees/5        
[Route("employees/{empId:int}")]        
public ActionResult Details(int empId)        
{            
     return View();        
}

Here, we are defining a constraint that the empId parameter must be an integer inorder to map this action method to the URI request.

Here is the list of all the constraints that are available to use.

Constraint Description Example
alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha}
bool Matches a Boolean value. {x:bool}
datetime Matches a DateTime value. {x:datetime}
decimal Matches a decimal value. {x:decimal}
double Matches a 64-bit floating-point value. {x:double}
float Matches a 32-bit floating-point value. {x:float}
guid Matches a GUID value. {x:guid}
int Matches a 32-bit integer value. {x:int}
length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
{x:length(1,20)}
long Matches a 64-bit integer value. {x:long}
max Matches an integer with a maximum value. {x:max(10)}
maxlength Matches a string with a maximum length. {x:maxlength(10)}
min Matches an integer with a minimum value. {x:min(10)}
minlength Matches a string with a minimum length. {x:minlength(10)}
range Matches an integer within a range of values. {x:range(10,50)}
regex Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)}

That’s all about the new Routing mechanism introduced in ASP.MVC 5.

Hope you enjoyed! Happy Coding!!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s