Route Constraints in ASP.NET MVC Attribute Routing are a set of rules applied to route parameters. These constraints help in controlling the format and values of parameters within a route. By using the ":" symbol, we can enforce specific conditions on route parameters. To understand how to implement Route Constraints, let’s look at an example. First, update the Students Controller as shown below.
namespace AttributeRoutingDemoInMVC.Controllers
{
[RoutePrefix("students")]
public class StudentsController : Controller
{
static List<student> students = new List<student>()
{
new Student() { Id = 1, Name = "Shaista" },
new Student() { Id = 2, Name = "Noorain" },
new Student() { Id = 3, Name = "Anam" },
new Student() { Id = 4, Name = "Arshiya" }
};
[HttpGet]
[Route("{studentID}")]
public ActionResult GetStudentDetails(int studentID)
{
Student studentDetails = students.FirstOrDefault(s => s.Id == studentID);
return View(studentDetails);
}
}
}
Next, create the GetStudentDetails view and insert the following code into it.
@model AttributeRoutingDemoInMVC.Models.Student
@{
ViewBag.Title = "GetStudentDetails";
}
<h2>GetStudentDetails</h2>
<div>
<h4>Student</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd>
@Html.DisplayFor(model => model.Name)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Id)
</dt>
<dd>
@Html.DisplayFor(model => model.Id)
</dd>
</dl>
</div>
When you navigate to the /students/1 URL, the GetStudentDetails(int studentID) action method executes, fetching the details of the student with ID 1 as expected. Now, let's modify the requirement. In addition to retrieving student details using the "student ID," we also want to fetch student details using the "student Name." To achieve this, we need to add another GetStudentDetails() method that accepts a string parameter, as shown below.
namespace AttributeRoutingDemoInMVC.Controllers
{
[RoutePrefix("students")]
public class StudentsController : Controller
{
static List<student> students = new List<student>()
{
new Student() { Id = 1, Name = "Shaista" },
new Student() { Id = 2, Name = "Noorain" },
new Student() { Id = 3, Name = "Anam" },
new Student() { Id = 4, Name = "Arshiya" }
};
[HttpGet]
[Route("{studentID}")]
public ActionResult GetStudentDetails(int studentID)
{
Student studentDetails = students.FirstOrDefault(s => s.Id == studentID);
return View(studentDetails);
}
[HttpGet]
[Route("{studentName}")]
public ActionResult GetStudentDetails(string studentName)
{
Student studentDetails = students.FirstOrDefault(s => s.Name == studentName);
return View(studentDetails);
}
}
}
Now, build the solution and try accessing the following URLs:
In both cases, you will encounter the following error.
This happens because the ASP.NET MVC Framework cannot determine which version of the GetStudentDetails() action method to invoke when a request is made. This is where route constraints become essential. If the URL contains an integer (/students/1), the framework should call the GetStudentDetails(int studentId) method, which takes an integer parameter. On the other hand, if the URL contains a string (/students/shaitsa), it should execute the GetStudentDetails(string studentName) method, which accepts a string parameter.
In an ASP.NET MVC application, this can be easily accomplished using Attribute Route Constraints. The syntax for specifying a route constraint is {parameter:constraint}. By applying these constraints, the framework determines which action method to invoke based on the parameter type. If the URL segment is an integer, the GetStudentDetails(int studentId) method is executed. If it's a string, the GetStudentDetails(string studentName) method is called.
Now,
let’s update the Student Controller to implement Attribute Route Constraints, as shown below, to meet these requirements.
namespace AttributeRoutingDemoInMVC.Controllers
{
[RoutePrefix("students")]
public class StudentsController : Controller
{
static List<student> students = new List<student>()
{
new Student() { Id = 1, Name = "Shaista" },
new Student() { Id = 2, Name = "Noorain" },
new Student() { Id = 3, Name = "Anam" },
new Student() { Id = 4, Name = "Arshiya" }
};
[HttpGet]
[Route("{studentID:int}")]
public ActionResult GetStudentDetails(int studentID)
{
Student studentDetails = students.FirstOrDefault(s => s.Id == studentID);
return View(studentDetails);
}
[HttpGet]
[Route("{studentName:alpha}")]
public ActionResult GetStudentDetails(string studentName)
{
Student studentDetails = students.FirstOrDefault(s => s.Name == studentName);
return View(studentDetails);
}
}
}
Now, build the solution and visit the following URLs to verify that everything is working correctly:
Keep in mind that "alpha" represents both uppercase and lowercase alphabetic characters. In addition to int and alpha, there are other constraints like decimal, double, float, long, and bool, as shown in the image below.
To ensure that the GetStudentDetails(int studentId) action method is mapped to the URI /students/{studentId} only when studentId is a number greater than zero, use the "min" constraint as demonstrated below.
[Route("{studentID:int:min(1)}")]
public ActionResult GetStudentDetails(int studentID)
{
Student studentDetails = students.FirstOrDefault(s => s.Id == studentID);
return View(studentDetails);
}
With this modification, if you enter a positive number like 1 in the URL, it will correctly map to the GetStudentDetails(int studentID) action method:
/students/1However, if you enter 0 or a negative number, an error will occur. For instance, if you set the studentID to 0 in the URL, you will encounter an error.
http://localhost:58316/students/0
You will get the below error
In addition to the "min" constraint, you can also use the "max" constraint. For example, if you want the studentID value in the URL to be between 1 and 3 (inclusive), you can apply both "min" and "max" constraints as shown below.
[HttpGet]
[Route("{studentID:int:min(1):max(3)}")]
public ActionResult GetStudentDetails(int studentID)
{
Student studentDetails = students.FirstOrDefault(s => s.Id == studentID);
return View(studentDetails);
}
The same functionality can be achieved using the "range" attribute, as demonstrated below.
[HttpGet]
[Route("{studentID:int:range(1,3)}")]
public ActionResult GetStudentDetails(int studentID)
{
Student studentDetails = students.FirstOrDefault(s => s.Id == studentID);
return View(studentDetails);
}