Introduction
Whenever a client sends an HTTP request to a .NET Core Web API, the application needs to identify which endpoint should process that request.
For example, imagine your Employee Management application has the following requirements:
Retrieve all employees.
Retrieve an employee using their ID.
Retrieve employees belonging to a particular department.
Search employees using their name.
Retrieve employees based on department and location.
How does the API distinguish between these requests?
This is where Routing comes into the picture.
Routing is the mechanism that matches an incoming HTTP request to an endpoint based on information such as the URL and HTTP method.
In this article, we'll understand routing step by step and implement practical examples using Controllers and Minimal APIs.
1. What is Routing in .NET Core Web API?
Routing is the process of matching an incoming HTTP request to an appropriate endpoint.
An endpoint is the destination in your application that handles a particular request.
For example:
GET /api/employees
GET /api/employees/101
GET /api/employees/department/ITAlthough all three requests belong to the Employee Management API, they perform different operations.
The routing system identifies the matching endpoint and forwards the request for execution.
Consider this simple controller:
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
[HttpGet]
public IActionResult GetEmployees()
{
return Ok("Get all employees");
}
[HttpGet("{id:int}")]
public IActionResult GetEmployeeById(int id)
{
return Ok($"Get employee with ID: {id}");
}
}Here, we have defined two endpoints:
| HTTP method | URL | Action |
|---|---|---|
| GET | /api/employees | GetEmployees |
| GET | /api/employees/101 | GetEmployeeById |
When a client requests /api/employees/101, routing matches the second action and provides 101 as the id parameter.
2. Understanding Attribute Routing
Attribute routing allows you to define routes directly on controllers and their action methods using attributes.
It is commonly used in controller-based Web APIs.
Let's understand the two important routing attributes.
A. Route attribute
The [Route] attribute defines the route pattern for a controller or action.
Example:
[Route("api/employees")]
public class EmployeesController : ControllerBase
{
}Now the controller's base route is:
/api/employeesYou can also use a controller token:
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
}Here, [controller] is replaced with the controller name without the Controller suffix.
Therefore, the resulting route is:
/api/employeesB. HTTP method attributes
HTTP method attributes define which HTTP method an action accepts.
Common attributes include:
[HttpGet]
[HttpPost]
[HttpPut]
[HttpPatch]
[HttpDelete]For example:
[HttpGet]
public IActionResult GetEmployees()
{
return Ok("Employee list");
}
[HttpPost]
public IActionResult CreateEmployee()
{
return Ok("Employee created");
}Both actions use the same route, but they handle different HTTP methods.
This is useful when designing RESTful APIs.
3. What are Route Parameters?
Route parameters allow you to pass dynamic values through the URL.
Suppose you want to retrieve an employee with ID 101.
Instead of creating a separate endpoint for every employee, you can define a dynamic route:
[HttpGet("{id}")]
public IActionResult GetEmployeeById(int id)
{
return Ok($"Employee ID: {id}");
}Now the same endpoint can handle:
GET /api/employees/101
GET /api/employees/102
GET /api/employees/103The framework extracts the value from the URL and binds it to the id parameter.
For example:
GET /api/employees/101The action receives:
id = 101;This is called route parameter binding.
Multiple route parameters
You can also define multiple parameters in a single route.
For example:
[HttpGet("{department}/{location}")]
public IActionResult GetEmployeesByDepartmentAndLocation(
string department,
string location)
{
return Ok(new
{
Department = department,
Location = location
});
}Request:
GET /api/employees/IT/DelhiThe framework binds:
department = IT
location = DelhiExpected response:
{
"department": "IT",
"location": "Delhi"
}This approach is useful when the URL structure naturally identifies the requested resource or its hierarchy.
4. What are Route Constraints?
Sometimes you want a route parameter to accept only a particular type of value.
For example, an employee ID should be an integer.
Without a route constraint:
[HttpGet("{id}")]
public IActionResult GetEmployee(int id)
{
return Ok(id);
}You can explicitly define the expected parameter type using a route constraint.
Example:
[HttpGet("{id:int}")]
public IActionResult GetEmployee(int id)
{
return Ok($"Employee ID: {id}");
}The :int constraint ensures that the route matches only when the supplied value is an integer.
For example:
GET /api/employees/101Matches the route.
But:
GET /api/employees/abcDoes not match this integer-constrained route.
If no other endpoint matches, the application returns a 404 Not Found response.
Common route constraints
| Constraint | Purpose | Example |
|---|---|---|
int | Integer value | {id:int} |
long | Long integer | {id:long} |
guid | GUID value | {id:guid} |
bool | Boolean value | {active:bool} |
alpha | Alphabetic characters | {name:alpha} |
min(value) | Minimum numeric value | {age:min(18)} |
max(value) | Maximum numeric value | {age:max(60)} |
range(min,max) | Numeric range | {age:range(18,60)} |
minlength(value) | Minimum string length | {name:minlength(3)} |
maxlength(value) | Maximum string length | {name:maxlength(20)} |
Practical example
[HttpGet("employee/{id:guid}")]
public IActionResult GetEmployeeByGuid(Guid id)
{
return Ok(new
{
EmployeeId = id
});
}This endpoint matches GUID values such as:
/api/employees/employee/550e8400-e29b-41d4-a716-446655440000Route constraints are useful for selecting the correct endpoint based on the URL structure and expected value format.
Important: Route constraints are not a replacement for business validation. For example, {age:int} checks that the value is an integer, but it does not establish whether that age is valid for your business rules.
5. What are Optional Route Parameters?
Sometimes a route parameter should be optional.
For example, you may want to retrieve all employees when no department is supplied, or retrieve employees belonging to a particular department when one is provided.
You can use ? to define an optional route parameter.
Example:
[HttpGet("department/{departmentName?}")]
public IActionResult GetEmployeesByDepartment(string? departmentName)
{
if (string.IsNullOrWhiteSpace(departmentName))
{
return Ok("Returning all employees");
}
return Ok($"Returning employees from {departmentName}");
}Now consider these requests:
Request 1:
GET /api/employees/departmentExpected result:
Returning all employeesRequest 2:
GET /api/employees/department/ITExpected result:
Returning employees from ITThe ? makes the route segment optional, while string? indicates that the parameter may be null.
In real applications, you should clearly define what happens when an optional value is missing.
6. What are Query String Parameters?
Query string parameters are values passed in the URL after the ? character.
They are commonly used for filtering, searching, sorting, and pagination.
For example:
GET /api/employees?department=ITHere:
departmentis the query parameter.ITis its value.
Unlike route parameters, query parameters are not part of the route path.
Practical example
[HttpGet("search")]
public IActionResult SearchEmployees([FromQuery] string department)
{
return Ok(new
{
Department = department
});
}Request:
GET /api/employees/search?department=ITThe framework binds IT to the department parameter.
Multiple query parameters
You can also send multiple query parameters.
Example:
GET /api/employees/search?department=IT&location=DelhiController action:
[HttpGet("search")]
public IActionResult SearchEmployees(
[FromQuery] string department,
[FromQuery] string location)
{
return Ok(new
{
Department = department,
Location = location
});
}Expected response:
{
"department": "IT",
"location": "Delhi"
}Route parameters vs Query parameters
Both are useful, but they serve different purposes.
| Comparison | Route parameters | Query parameters |
|---|---|---|
| Location | Inside URL path | After ? |
| Common purpose | Identify a resource | Filter or search resources |
| Example | /employees/101 | /employees?department=IT |
| Required by route | Depending on route definition | Usually optional |
| Multiple values | Defined as route segments | Joined using & |
Practical recommendation:
Use route parameters when identifying a specific resource.
Use query parameters when filtering or modifying how a collection is retrieved.
For example:
GET /api/employees/101
GET /api/employees?department=IT
GET /api/employees?page=1&pageSize=107. Routing in Minimal APIs
So far, we have explored routing through Controllers.
Minimal APIs also support routing, but instead of using controller attributes, they use endpoint mapping methods.
For example:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Get all employees
app.MapGet("/api/employees", () =>
{
return Results.Ok("All employees");
});
// Get employee by ID
app.MapGet("/api/employees/{id:int}", (int id) =>
{
return Results.Ok($"Employee ID: {id}");
});
// Get employees by department
app.MapGet("/api/employees/department/{department}",
(string department) =>
{
return Results.Ok($"Department: {department}");
});
// Search employees using query parameters
app.MapGet("/api/employees/search",
(string department, string location) =>
{
return Results.Ok(new
{
Department = department,
Location = location
});
});
app.Run();Let's understand these examples.
Example 1:
app.MapGet("/api/employees", ...);Maps a GET request to the employee collection.
Example 2:
app.MapGet("/api/employees/{id:int}", ...);Maps a GET request with an integer route parameter.
Example 3:
app.MapGet("/api/employees/department/{department}", ...);Maps a GET request with a department route parameter.
Example 4:
app.MapGet("/api/employees/search",
(string department, string location) => ...);The department and location values are inferred from query parameters because they are not route parameters or otherwise supplied from another binding source.
Request:
GET /api/employees/search?department=IT&location=DelhiMinimal APIs provide a straightforward way to define route patterns and bind incoming values to endpoint parameters.
8. Route Groups in Minimal APIs
As the number of endpoints increases, you may notice that many endpoints share the same URL prefix.
For example:
/api/employees
/api/employees/101
/api/employees/department/ITInstead of repeating the common prefix, Minimal APIs provide MapGroup().
Example:
var employeeGroup = app.MapGroup("/api/employees");
employeeGroup.MapGet("/", () =>
{
return Results.Ok("All employees");
});
employeeGroup.MapGet("/{id:int}", (int id) =>
{
return Results.Ok($"Employee ID: {id}");
});
employeeGroup.MapGet("/department/{department}",
(string department) =>
{
return Results.Ok($"Department: {department}");
});All endpoints share the /api/employees prefix.
This makes endpoint organization easier, especially when an application has multiple API modules.
Route groups can also be used to apply shared endpoint configuration, such as authorization requirements, to a group of endpoints.
9. How does .NET Core Web API select the correct route?
Imagine you have the following endpoints:
[HttpGet("{id:int}")]
public IActionResult GetEmployeeById(int id)
{
return Ok($"Employee ID: {id}");
}
[HttpGet("search")]
public IActionResult SearchEmployees()
{
return Ok("Search employees");
}Now consider these requests:
GET /api/employees/101
GET /api/employees/searchThe routing system considers the available route patterns and their constraints to identify the appropriate endpoint.
The first request matches the integer route.
The second request matches the literal search route.
Endpoint selection is based on route matching, HTTP method, constraints, and endpoint metadata—not simply on the order in which controller methods appear in the file.
If no suitable endpoint matches, the application generally returns 404 Not Found.
If a path matches an endpoint but the HTTP method is not supported, the application can return 405 Method Not Allowed.
This distinction is useful when debugging routing problems.
10. Common routing mistakes developers should avoid
1. Forgetting the controller route prefix
If your controller has:
[Route("api/[controller]")]Remember that the resulting URL includes the api prefix and controller name.
For EmployeesController, the route starts with /api/employees.
2. Using route parameters when query parameters are more suitable
For filtering a collection, a query string is often clearer:
GET /api/employees?department=ITInstead of creating a separate route for every possible filter.
3. Forgetting route constraints
If an endpoint expects an integer ID, adding {id:int} makes the intended route requirement explicit.
4. Confusing route parameters with query parameters
These are different request values:
GET /api/employees/101
GET /api/employees?id=101The first uses a route parameter. The second uses a query parameter.
5. Creating ambiguous routes
Avoid defining multiple endpoints with overlapping route patterns and the same HTTP method if the framework cannot distinguish them.
Design routes with clear and predictable patterns.
6. Using route constraints as business validation
An integer constraint verifies the URL value's format. It does not verify whether an employee exists or whether the value satisfies business rules.
11. Interview questions
Q1. What is routing in .NET Core Web API?
Routing is the process of matching incoming HTTP requests to the appropriate endpoints based on information such as the URL and HTTP method.
Q2. What is attribute routing?
Attribute routing allows developers to define routes directly on controllers and action methods using attributes such as [Route], [HttpGet], and [HttpPost].
Q3. What is the difference between route parameters and query parameters?
Route parameters are part of the URL path and are commonly used to identify resources. Query parameters appear after the ? character and are commonly used for filtering, searching, and pagination.
Q4. What is a route constraint?
A route constraint restricts the values that can match a route parameter. For example, {id:int} matches integer values.
Q5. How do you define an optional route parameter?
You can use ? in the route template.
Example:
[HttpGet("department/{name?}")]Q6. What is the purpose of [FromQuery]?
[FromQuery] explicitly tells the framework to bind a parameter from the query string.
Q7. What is the purpose of [controller] in routing?
It is a route token that is replaced with the controller name without the Controller suffix.
Q8. What is MapGroup() in Minimal APIs?
MapGroup() allows related endpoints to share a common route prefix and endpoint configuration.
Q9. What is the difference between 404 and 405?
404 Not Found indicates that no matching resource or endpoint was found. 405 Method Not Allowed indicates that the target resource does not support the requested HTTP method.
Q10. Can we use both route parameters and query parameters in the same endpoint?
Yes. For example:
GET /api/employees/101?includeDepartment=trueHere, 101 identifies the employee, while includeDepartment is a query parameter.
Conclusion
Routing is one of the fundamental concepts in .NET Core Web API development.
It helps the application identify the correct endpoint for every incoming request.
In this article, we explored:
Attribute routing
Route parameters
Multiple route parameters
Route constraints
Optional route parameters
Query string parameters
Routing in Minimal APIs
Route groups
We also learned how route parameters and query parameters are used for different purposes when designing API URLs.
Understanding these concepts will help you create clean, predictable, and maintainable API endpoints.
What's next?
In Day 6, we'll move from individual routing concepts to practical implementation.
We'll Build Your First .NET Core Web API step by step, create a new project, configure the application, implement employee endpoints, run the API, and test it using an API client.
We'll bring together the concepts we've learned so far and build our first complete working API project.
Skills Tutorials – Learn • Build • Grow Together
Continue learning with our 30-day .NET Core Web API series.