Attribute Routing in ASP.NET Core MVC

Attribute Routing in ASP.NET Core MVC Application

In this article, I will discuss Attribute Routing in ASP.NET Core MVC Web Applications with Examples. Please read our previous article, discussing How to Create Custom Route Constraints in ASP.NET Core MVC Web Applications. As part of this article, we will discuss the following pointers.

  1. What is Attribute Routing in ASP.NET Core MVC?
  2. Why do We Need Attribute Routing in ASP.NET Core MVC Applications?
  3. How do We Use Attribute Routing in ASP.NET Core MVC?
  4. Can we Apply Multiple Route Attributes to a Single Action Method in ASP.NET Core MVC?
  5. Attribute Routing with Parameters in ASP.NET Core MVC Application.
  6. Attribute Routing with Optional Parameters in ASP.NET Core.
  7. Can we use both Attribute and Convention-Based Routing in a Single ASP.NET Core MVC Application?
  8. How Do We Apply Route Attributes at the Controller Level in the ASP.NET Core MVC application?
  9. How Do We Ignore the Route Attribute Placed at the Controller Level in ASP.NET Core MVC?
  10. Defining Route using HTTP Methods.
  11. What is the Difference Between [Route] and [HttpGet], [HttpPost], etc., Attributes?
  12. What are the Advantages of using Attribute Routing in ASP.NET Core MVC?
What is Attribute Routing in ASP.NET Core MVC?

Attribute routing is a feature in ASP.NET Core MVC that allows you to define routes directly on your controller and action methods using Route attributes. That means if we define the Routes using the [Route] attribute either at the controller or the action method level, it is called Attribute Routing. Attribute Routing gives us more control over the URIs by defining routes directly at action methods or controllers in our ASP.NET Core MVC Web Application. 

This approach provides a way to specify the URL patterns an application responds to directly in the code rather than define them separately in a routing configuration. It gives developers more control over the URLs of their web applications, making the routes more visible and centralized within controller classes.

The key Concepts used in attribute routing are:

  • [Route]: Defines the URL pattern that a controller or action should handle. When applied to a controller, it specifies the base URL for all actions within that controller. When applied to an action, it specifies the URL pattern for just that action.
  • [HttpGet], [HttpPost], [HttpPut], [HttpDelete], [HttpPatch]: Specify the HTTP methods that an action supports. These attributes can also include templates for routing.

Note: The Convention-Based routing is still fully supported by ASP.NET Core MVC. In fact, we can use both these routing approaches in the same project. If both Routings are present, then .NET Core overrides conventional routes. This is because .NET Core gives preference to attribute routes.

Why do we need Attribute Routing in ASP.NET Core MVC Applications?

Let us understand the need for Attribute Routing in the ASP.NET Core MVC Web Application with an example. We will work with the same example we created in our Routing in ASP.NET Core MVC articles.

Adding Student Model:

First, add a class within the Models folder named Student.cs. So, please right-click on the Models folder and then add a class file with the name Student.cs, and then copy and paste the following code.

namespace RoutingInASPDotNetCoreMVC.Models
{
    public class Student
    {
        public int Id { get; set; }
        public string? Name { get; set; }
    }
}
Modifying Student Controller:

We have already created the Student controller within the Controllers folder. If not, please right-click on the Controllers folder and add a new ASP.NET Core MVC Empty controller named StudentController.cs, and then copy and paste the following code.

using Microsoft.AspNetCore.Mvc;
using RoutingInASPDotNetCoreMVC.Models;

namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class StudentController : Controller
    {
        //This is going to be our data source
        //In Real-Time you will get the data from the database
        static List<Student> students = new List<Student>()
        {
            new Student() { Id = 1, Name = "Pranaya" },
            new Student() { Id = 2, Name = "Priyanka" },
            new Student() { Id = 3, Name = "Anurag" },
            new Student() { Id = 4, Name = "Sambit" }
        };
       
        //This method is going to return all the Students
        public List<Student> GetAllStudents()
        {
            return students;
        }

        //This method is going to return a student based on the student id
        public Student GetStudentByID(int studentID)
        {
            //Returning the First Student Information
            Student? studentDetails = students.FirstOrDefault(s => s.Id == studentID);
            return studentDetails ?? new Student();
        }

        //This method is going to return the courses of a student based on the student id
        public List<string> GetStudentCourses(int studentID)
        {
            //Real-Time you will get the courses from database, here we have hardcoded the data
            List<string> CourseList = new List<string>();
            if (studentID == 1)
                CourseList = new List<string>() { "ASP.NET Core", "C#.NET", "SQL Server" };
            else if (studentID == 2)
                CourseList = new List<string>() { "ASP.NET Core MVC", "C#.NET", "ADO.NET Core" };
            else if (studentID == 3)
                CourseList = new List<string>() { "ASP.NET Core WEB API", "C#.NET", "Entity Framework Core" };
            else
                CourseList = new List<string>() { "Bootstrap", "jQuery", "AngularJs" };
            
            return CourseList;
        }
    }
}
Modifying Program.cs Class file:

Next, modify the Main method of the Program class as follows. This is the default class file you will get when creating a new ASP.NET Core Web Application using the Model-View-Controller Project template. Here, we have configured the MVC Middleware Component and the Convention-Based Routing.

namespace RoutingInASPDotNetCoreMVC
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action}/{id:int?}",
                defaults: new { controller = "Home", action = "Index" }
            );

            app.Run();
        }
    }
}

Now, you can access the three action methods of the Student Controller using the following URLs, and it will work as expected.
https://localhost:44359/Student/GetAllStudents
https://localhost:44359/Student/GetStudentByID?studentID=1
https://localhost:44359/Student/GetStudentCourses?studentID=1

In some scenarios, Convention-Based routing makes it very difficult to support certain URL patterns. But those URL patterns can be easily achieved by using the Attribute Routing in ASP.NET Core MVC Application. 

In our example, If we want the URL Pattern “/student/1/courses” and if that URL Pattern should be mapped to GetStudentCourses(int studentID), then this type of URL Pattern is very difficult to create using the convention-based routing in ASP.NET Core MVC Application. But, by using Attribute Routing in ASP.NET Core MVC Application, we can easily achieve this type of URL pattern.

How Do We Use Attribute Routing in ASP.NET Core MVC?

Using attribute routing in ASP.NET Core MVC is straightforward. Attribute routing allows us to define routes directly on our controller and action methods using the Route Attributes. With the help of ASP.NET Core Attribute Routing, we can use the Route attribute to define routes for our application. We can use the Route attribute either at the Controller or Action Methods levels. Applying the Route attribute at the Controller level applies to all the controller’s action methods. Let us first modify the Student Controller as shown below. Here, we have applied the Route Attribute at the Action methods level.

using Microsoft.AspNetCore.Mvc;
using RoutingInASPDotNetCoreMVC.Models;

namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class StudentController : Controller
    {
        //This is going to be our data source
        //In Real-Time you will get the data from the database
        static List<Student> students = new List<Student>()
        {
            new Student() { Id = 1, Name = "Pranaya" },
            new Student() { Id = 2, Name = "Priyanka" },
            new Student() { Id = 3, Name = "Anurag" },
            new Student() { Id = 4, Name = "Sambit" }
        };

        //This method is going to return all the Students
        //URL Pattern: /Student/All
        [Route("Student/All")]
        public List<Student> GetAllStudents()
        {
            return students;
        }

        //This method is going to return a student based on the student id
        //URL Pattern: /Student/1/Details
        [Route("Student/{studentID}/Details")]
        public Student GetStudentByID(int studentID)
        {
            //Returning the First Student Information
            Student? studentDetails = students.FirstOrDefault(s => s.Id == studentID);
            return studentDetails ?? new Student();
        }

        //This method is going to the courses of a student based on the student id
        //URL Pattern: /Student/1/Courses
        [Route("Student/{studentID}/Courses")] 
        public List<string> GetStudentCourses(int studentID)
        {
            //Real-Time you will get the courses from database, here we have hardcoded the data
            List<string> CourseList = new List<string>();
            if (studentID == 1)
                CourseList = new List<string>() { "ASP.NET Core", "C#.NET", "SQL Server" };
            else if (studentID == 2)
                CourseList = new List<string>() { "ASP.NET Core MVC", "C#.NET", "ADO.NET Core" };
            else if (studentID == 3)
                CourseList = new List<string>() { "ASP.NET Core WEB API", "C#.NET", "Entity Framework Core" };
            else
                CourseList = new List<string>() { "Bootstrap", "jQuery", "AngularJs" };
            
            return CourseList;
        }
    }
}

With the above Attribute Routing in Place, we can now access the above three action methods of the Student Controller using the following URLs.
https://localhost:44359/Student/All
https://localhost:44359/Student/1/Details
https://localhost:44359/Student/1/Courses

Can we Apply Multiple Route Attributes to a Single Action Method in ASP.NET Core MVC?

Yes, we can apply Multiple Route Attributes to a Single Action Method in the ASP.NET Core MVC Application. Let us understand this with an example. Please modify the Home Controller as shown below, and please have a look at the Index Action method, where we have applied three attributes.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("Home")]
        [Route("Home/Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }
    }
}

If you notice, here we have applied the Route() attribute 3 times on the Index() action method of the Home Controller class. Here, we specified a different route template with each instance of the Route attribute. With the above three Route attributes applied to the Index action method, we can now access the Index() action method of the HomeController using the following 3 URLs.
https://localhost:44359/
https://localhost:44359/home
https://localhost:44359/home/index

If you apply the same Route Attributes multiple times with an Action Method and if you apply the same Route Template to different action methods, then you will get AmbiguousMatchException. That means the request matched multiple endpoints. Now run the application and navigate to the above three URLs, and you will see the Home page as expected.

Attribute Routing with Parameters in ASP.NET Core MVC Application:

As we already discussed, conventional-based routing can specify the route parameters as part of the route template. We can also do the same with Attribute Routing in ASP.NET Core. That means we can also define Route Attributes with Route Parameters. To understand this, modify the Home Controller as shown below.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("Home")]
        [Route("Home/Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [Route("Home/Details/{id}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }
    }
}

As you can see in the above code, the Details() action method has the id parameter. Notice that in the route template, we also specified the ID parameter ([Route(“Home/Details/{id}”)]). So, the URL (/Home/Details/10) will execute the Details(int id) action method and map the value 10 to the id parameter of the Details action method. This is done by a process called Model binding, which will be discussed in our upcoming articles. Run the application and navigate to the following URL, and you should see the output as expected.
https://localhost:44359/Home/Details/10

Attribute Routing with Optional Parameters in ASP.NET Core MVC Application:

We can also make the Route Parameter optional in Attribute Routing like conventional-based routing. You can define a Route Parameter as optional by adding a question mark (?) to the route parameter. You can also specify the default value by using the parameter = value. If this is not clear at the moment, then don’t worry; we will see both of these approaches with examples.

In our example, at the moment, the Details(int id) action method of the HomeController is executed only if we pass the id parameter value. If we do not pass the id parameter value, we will get 404. For example, at the moment, if we navigate to the following URL, we will get a 404 error.
https://localhost:44359/Home/Details

Let us modify the Route attribute of the Details action method as shown below to make the route parameter id optional by adding “?” at the end, i.e. [Route(“Home/Details/{id?}”)]

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("Home")]
        [Route("Home/Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [Route("Home/Details/{id?}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }
    }
}

Now run the application and navigate to the following URL, and you will see the output as expected instead of a 404 error.
https://localhost:44359/Home/Details

We can also make the parameter optional by specifying a default value. For a better understanding, please modify the Home Controller class as follows. Within the Route Attribute of Details action method, we have specified the default value for the id parameter as 10.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("Home")]
        [Route("Home/Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [Route("Home/Details/{id=10}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }
    }
}

If you run the application and navigate to the URL below without specifying the ID parameter, it will take the default value of 10.
https://localhost:44359/Home/Details

Controller and Action Method Names in Attribute Routing:

With Attribute Routing in ASP.NET Core MVC Application, the Controller name and Action Method names do not play any role. To understand this, modify the Home Controller as shown below.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("MyHome")]
        [Route("MyHome/Index")]
        public string StartPage()
        {
            return "StartPage() Action Method of HomeController";
        }
    }
}

As you can see, we have specified the Route attribute three times with the StartPage() action method of the HomeController. So, this StartPage action method will be executed for the following 3 URLs.
https://localhost:44359/
https://localhost:44359/home
https://localhost:44359/home/index

Can we use both Attribute and Convention-Based Routing in a Single ASP.NET Core MVC Application?

Yes. Both routing mechanisms can be combined in a single ASP.NET Core MVC Web Application. The controller action methods with the [Route] attribute use Attribute Routing, and those without the [Route] attribute use Convention-based routing. For a better understanding, please modify the Home Controller class as shown below.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("MyHome")]
        [Route("MyHome/Index")]
        public string StartPage()
        {
            return "StartPage() Action Method of HomeController";
        }

        public string Privacy()
        {
            return "Privacy() Action Method of HomeController";
        }
    }
}

As you can see in the above code, we have applied the Route Attribute with the StartPage action method, and we have not applied the Route Attribute with the Privacy action method. So, in this case, StartPage will use Attribute Routing, and we can access this method using the following 3 URLs.
https://localhost:44359/
https://localhost:44359/home
https://localhost:44359/home/index

On the other hand, the Privacy action method will use Contention-Based Routing and can be accessed by using the following URL only.
https://localhost:44359/home/privacy

Note: It is not possible to access an action method using both Attribute and Convention Routing. If Attribute Routing is applied to an action method, then you can access that method using Attribute Routing only; you cannot access the same method using Convention Routing. Similarly, if Attribute Routing is not applied to an action method, then you can access that method using Convention Routing only; you cannot access the same method using Attribute Routing.

Applying Route Attribute at the Controller Level in the ASP.NET Core MVC:

In the ASP.NET Core MVC Web Application, applying the Route() Attribute at the Controller class and on individual action methods is possible. If you want to make the attribute routing less repetitive, then you need to use the Route Attribute on the controller level. Let us understand this with an example. First, modify the Home Controller class as shown below. Here, we have created the Home controller with two action methods.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        [Route("")]
        [Route("Home")]
        [Route("Home/Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }
        [Route("Home/Details/{id?}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }
    }
}

With the above code in place, we can access the Index() action method using the following 3 URLs.
/
/Home
/Home/Index

Along the same line, we can also access the Details(int? id) action method using the following 2 URLs. 
/Home/Details
/Home/Details/2 

If you look at our code, we are using Route Attributes at the action method level to define routes, and further, if you notice, all the routes in the HomeController start with the same prefix, i.e., Home. That means Home is the common prefix for all the routes in the Home Controller.

Here, instead of writing the common prefix Home at each action method, we can specify the Home for the Home Controller (for all the action methods of the Home controller) using the [Route] attribute at the controller level.

So, modify the Home Controller class as follows. Here. You can see we have applied [Route(“Home”)] at the controller level, and from each action method Route Attribute, we have removed that Home prefix. The Route Attribute that we define at the Controller level will be applied to each action method.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    [Route("Home")]
    public class HomeController : Controller
    {
        [Route("")]
        [Route("Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [Route("Details/{id?}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }
    }
}

The Route template applied on the controller level is prepended to the route template applied to the action method level. When you navigate to the following four URLs, you will get the output as expected.
https://localhost:44359/home
https://localhost:44359/home/index
https://localhost:44359/home/details
https://localhost:44359/home/details/10

However, when you navigate to the application’s root URL (http://localhost:44359), you will get a 404 error. To solve this, you need to include the route template that begins with “/” i.e. [Route(“/”)] on the Index() action method, as shown in the image below. 

Attribute Routing in ASP.NET Core MVC Web Applications with Examples

So, modify the Home Controller as follows:

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    [Route("Home")]
    public class HomeController : Controller
    {
        [Route("")]
        [Route("/")]
        [Route("Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [Route("Details/{id?}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }
    }
}

With the above changes in place, now run the application and navigate to the root URL to see the expected output. As you can see in the above code, we have applied the [Route(“Home”)] attribute at the Controller level. This Route attribute eliminates the need to repeat the common prefix “Home” on each and every controller action method. However, sometimes, we may need to override the route attribute defined at the controller level. 

How Do We Ignore the Route Attribute Placed at the Controller Level in ASP.NET Core MVC?

To ignore the Route Template placed at the Controller level, you must use / or ~/ at the action method level. If the action method route template starts with / or ~/, then the controller route attribute will not be combined with the action method route attribute. To understand this, let us modify the Home Controller class as shown below. In the following code, the About action method starts with ~/, so this action method will not be combined with the controller route attribute.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    [Route("Home")]
    public class HomeController : Controller
    {
        [Route("")]
        [Route("/")]
        [Route("Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [Route("Details/{id?}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }

        [Route("~/About")]
        public string About(int id)
        {
            return "About() Action Method of HomeController";
        }
    }
}

Now run the application and navigate to /About URL, and you will see the output as expected.

Defining Route using HTTP Methods:

We can also achieve the same using HTTP Verbs. So, let us rewrite the previous example using HTTP Methods as follows:

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    [Route("Home")]
    public class HomeController : Controller
    {
        [HttpGet("")]
        [HttpGet("/")]
        [HttpGet("Index")]
        public string Index()
        {
            return "Index() Action Method of HomeController";
        }

        [HttpGet("Details/{id?}")]
        public string Details(int id)
        {
            return "Details() Action Method of HomeController, ID Value = " + id;
        }

        [HttpGet("~/About")]
        public string About(int id)
        {
            return "About() Action Method of HomeController";
        }
    }
}
What is the Difference Between [Route] and [HttpGet], [HttpPost], etc., Attributes?

The [Route] attribute specifies a route template for a controller or action method but does not imply any specific HTTP method. On the other hand, HTTP method attributes like [HttpGet], [HttpPost], [HttpPut], and [HttpDelete] not only define the route template but also restrict the action method to be invoked only with the specified HTTP method.

What are the Advantages of using Attribute Routing in ASP.NET Core MVC?

Attribute routing in ASP.NET Core MVC applications provides a way to define routes directly on actions and controllers rather than using a separate routing configuration. This method offers several benefits that enhance the development and maintenance of web applications:

  • Explicit Route Definition: Attribute routing allows you to define routes directly on your controller and action methods using attributes. This makes the routing configuration more explicit and self-contained, improving the readability and maintainability of your code. Each action’s URL pattern is defined right where the action is, making it easier to understand the routing logic.
  • Increased Control and Clarity: Attribute routing allows developers to see the routes directly above the action methods in the controller, making it easier to understand which routes lead to which actions. This proximity increases the readability and maintainability of the code, as developers can easily identify and modify routes and their corresponding actions.
  • Flexibility in Route Definition: With attribute routing, developers can define complex route patterns that are closely aligned with the actions they serve. This includes the ability to specify optional parameters, constraints, and default values within the route itself, offering a granular level of control over how URLs are matched to actions.
  • RESTful API Design: Attribute routing is well-suited for creating RESTful APIs. You can easily define routes that follow REST conventions, such as /api/products/{id} for retrieving a specific product or /api/orders/{orderId}/items/{itemId} for managing order items. This leads to more meaningful and predictable URLs in your API.
  • Improved Route Customization: Attribute routing enables developers to customize routes more extensively, including the ability to define multiple routes for a single action. This is particularly useful for supporting legacy URLs or when an application needs to be accessible via multiple paths.
  • Direct Route Constraints: Developers can apply constraints directly to routes defined with attributes, ensuring that only requests meeting specific criteria (e.g., HTTP method, data type, value range) are routed to the action method. This enhances the security and reliability of the application by preventing undesired requests from reaching action methods.
  • Dynamic and Custom Route Parameters: Attribute routing supports dynamic route parameters, allowing you to capture values from different URL parts, such as the query string, route segments, or even the request headers. This enables advanced scenarios where you need to extract information from various sources.
  • Better Separation of Concerns: Attribute routing can enhance the separation of concerns in your application. Routing information is directly associated with the relevant actions, reducing the need to manage routing configurations separately from your controller logic.
  • Clearer API Versioning: If you’re building APIs, attribute routing can be especially useful for versioning. You can use attribute routes to define version-specific routes and maintain better separation between different API versions.

That’s it for today. In the next article, I will discuss Tokens in Attribute Routing in ASP.NET Core MVC Applications. In this article, I try to explain Attribute Routing in the ASP.NET Core MVC Web Application. I hope you enjoy this Attribute Routing in ASP.NET Core MVC article.

4 thoughts on “Attribute Routing in ASP.NET Core MVC”

Leave a Reply

Your email address will not be published. Required fields are marked *