Attribute Routing in MVC and Web API - exploring the goodness

In this post you will learn about attribute routing and its goodness in MVC and Web API. In the beginning I will show you what it is by comparing it with Convention-based routing. As I progress in the post, I will show you its advantages and its configuration then I will show you how we can use it in the application by using various examples.

What is Routing?

Routing in MVC and Web API is a pattern matching system that matches incoming request and figure out what to do with that request. Inside application Routing engine uses defined Route table for matching URI to an action. We have opportunity to define one or more routes in the route table. We register this route table in Application_Start (Global.asax) using following code.

RouteConfig.RegisterRoutes(RouteTable.Routes);

Let’s look at default route for MVC and Web API, this is known as ‘Convention-based Routing’ system.


This ‘Convention-based routing’ routing system is awesome with certain limitations. Before MVC 5 and Web API 2 Core 5, routing was only handled conventionally by adding route to RouteCollection (in MVC RouteConfig.cs file) and HttpRouteCollection (in Web API WebApiConfig.cs file).

As website grows and special cases accumulate it starts turning quite complex to maintain this. And since routes (Convention-based routes) separated from the controller they apply to, it can take some detective work to understand the relationships and we are developers not detectivesJ.

So, with MVC 5 and Web API 2 Core 5, Microsoft introduced ‘Attribute-based Routing’ or simply ‘Attribute Routing’. It simplifies the Routing system that we earlier using in MVC as well as Web API. Attribute Routing uses attribute/annotation with controllers as well as action methods to define routes and gives way more control over the URIs. The earlier routing style is still fully supported, in fact we can easily combine both techniques in the same application.

Attribute Routing - one of the community contribution

As we all know Microsoft accepts community contribution and Attribute Routing in ASP.NET MVC and Web API is one of the contribution by Tim McCall, the author of http://attributerouting.net.


Attribute Routing or Convention-based Routing - what to use?

I personally recommend using ‘Attribute Routing’ however you can use any or both. In my all new applications I use one default route (Convention-based) so that it keeps my application alive even when I miss annotating controller/action with this new Attribute-based routing.

Can we delete earlier Convention-based Routing?

Yes we can delete earlier Convention-based Routing and stick with new ‘Attribute Routing’ if we want to but remember to annotate each controllers/actions with latest attribute routing, it will not kill your application.

If you are still not convinced, you should read this post by Kevin Montrose Why I Love Attribute Based Routing.


Enabling Attribute Routing in the application

To use Attribute Routing all you need is listed below.

Visual Studio 2013
MVC 5 (if MVC Application)
Microsoft ASP.NET Web API 2 Core 5 (if Web API Application)

If MVC Application, then you manually need to add following code in RouteConfig.cs file.


If Web API Application, then you manually need to add following code in WebApiConfig.cs file.


Look at both images (MVC Routing and Web API Routing) above, I have combined Attribute Routing with Convention-based routing and it works. I can completely delete anyone if I wanted it will not break application if no any controller/action depend directly.

Now you are good to use Attribute Routing in your MVC and Web API Applications.

I found many stack-overflow questions asking, do we need to install this package by Tim McCall in order to use Attribute Routing in MVC or Web API. Answer is ‘NO’, if you have MVC 5 or Web API 2 Core 5, you no longer need this package because it is already available in your application as a core bits.



Using Attribute Routing in the application (MVC or Web API)

Now let’s start using Attribute Routing in MVC and Web API Applications. I will show examples using MVC and Web API side by side. Look at the following code, this is the default code that uses Convention-based Routing.


Now let’s turn this code into Attribute-based Routing.


Once this in place I can go and delete the Convention-based Routes if I wanted to.


You don’t need to delete at all, it will still work. If you notice I have added routes.MapMvcAttributeRoutes() and config.MapHttpAttributeRoutes() before the Convention-based Routes, this tells runtime to first look at Attribute-based Routes in controllers and actions if it is not there make use of Convention-based default routes. This is how system works.

Let’s go ahead and explore something cooler.


RoutePrefix in Attribute Routing - to avoid code repetition

In case you don’t want to repeat the route prefix every time with action methods like we repeated in above image, we can use RoutePrefix attribute with controller directly as given below.


This way we can avoid repeating same code and do things DRY. Look at the Edit action method, it uses id parameter [Route("Edit/{id}")], we can also use such parameters with RoutePrefix if needed, example [RoutePrefix("{Author}/Books/{Id}")].


RoutePrefix Overriding in Attribute Routing

You can also override the RoutePrefix, this is required when you need every action methods to use RoutePrefix and any method not to use that. Here is the way.

[RoutePrefix("Books")]
public class BooksController : Controller
{
    // GET: /CS/Abhimanyu/1988
    [Route("~/CS/{author}/{id}")]
    public ActionResult Index()
    {
        return View();
    }
}

In above code, to hit this action we need to browse


Route Constraints in Attribute Routing

Unlike Convention-based Routing it is possible to use route constraints with Attribute Routing. Read my one of the article, if you don’t know anything about Route Constraints with Convention-based Routing.

Basically, Route constraints lets us restrict how the parameters in the route template are matched. The general syntax is "{parameter:constraint}".

Look at following code snippet, it will accept any integer ‘id’ for Details method.

[RoutePrefix("Books")]
public class BooksController : Controller
{
    // GET: /Books/Details/5
    [Route("Details/{id}")]
    public ActionResult Details(int id)
    {
        return View();
    }
}

Let’s assume if I want to restrict the ‘id’ value to 1 to 1000, we can achieve this also.

[RoutePrefix("Books")]
public class BooksController : Controller
{
    // GET: /Books/Details/5
    [Route("Details/{id:range(1,1000)}")]
    public ActionResult Details(int id)
    {
        return View();
    }
}

There is list of constraints we can use and you can find it here. We can also create custom route constraint by implementing the IHttpRouteConstraint interface and registering it with runtime.


Optional URI Parameters and Default Values in Attribute Routing

We can mark any parameter optional by adding a question mark to the route parameter. If a route parameter is optional, you must define a default value for the method parameter.

[Route("Books/Details/{id:int?}")]
public ActionResult Details(int id=1)
{
    return View();
}

In the above example, I marked ‘id’ as integer optional and also a default value in the method parameter. In case request has no ‘id’, this default value ‘1’ will be used.

So, /Books/Details/876 and /Books/Details will return same resource.

Alternatively, we can define default value with route annotation directly.

[Route("Books/Details/{id:int=1}")]
public ActionResult Details(int id)
{
    return View();
}

The main difference between first snippet and second snippets is, first will assign ‘1’ directly to the method parameter, so the parameter will have this exact value. Second will assign ‘1’ to annotation which will follow the model binding process for value conversion.


Route Names in Attribute Routing

Unlike Named Routes in Convention-based Routing we can define Route name for Attribute Routing. Here is a great blog by me on Convention-based Routing, read it in case you missed.

We can specify a name for a route and this name can be used to general URL easily. Look at this code snippets I have used ‘bookDetails’ as route name.

[Route("Details/{id}", Name="bookDetails")]
public ActionResult Details(int id=1)
{
    return View();
}

Now, we can use it to generate link, this way.

@Html.RouteLink("Book Details", "bookDetails", new { id = 1 })

This link will take you to following page http://localhost:9581/Books/Details/1.

Hope this helps.

Comments

  1. Good one , first time i know about attributed Routs by your article and its relay amazing feature..
    good going..

    ReplyDelete
  2. Can You please tell me the way or syntax to add validation for two or more parameters of a method/action

    ReplyDelete

Post a Comment

Popular posts from this blog