Back to: ASP.NET Core Web API Tutorials
Route Parameters and Query Strings in ASP.NET Core Web API Routing
In this article, I will discuss Working with Route Parameters and Query Strings in ASP.NET Core Web API Routing with Examples. Please read our previous article on Routing in ASP.NET Core Web API Applications. We will also work with the same example we created in our previous article.
Route Data and Query Strings in ASP.NET Core Web API Routing
When developing real-time RESTful services with ASP.NET Core Web API, handling dynamic data such as fetching order details by ID, retrieving employee information, searching books by author, and retrieving products by ID is essential. In ASP.NET Core Web API, Route Data and Query Strings are the two primary mechanisms for passing dynamic data from the client to the server.
Route Data in ASP.NET Core Web API Routing
Route Data is used to pass information as part of the URL path. It is typically used to identify specific resources or a set of resources, such as IDs or names, that are essential for locating specific data. Route parameters are embedded in the URL path itself and are often required to complete the request.
Use route parameters for mandatory information identifying or locating a resource (e.g., an employee ID or an order number). Route parameters are typically required for the endpoint to function correctly. If a route parameter is missing, the route does not match, and the action method will not be executed. So, missing route parameters will result in a 404 Not Found error.
Example to Understand Route:
Let’s create a simple API to manage employees. We will use In-Memory Data to simulate a data store.
Define the Employee Model
Create a class file named Employee.cs within the Models folder, and then copy and paste the following code:
namespace RoutingInASPNETCoreWebAPI.Models { public class Employee { public int Id { get; set; } public string Name { get; set; } public string Gender { get; set; } public string Department { get; set; } public string City { get; set; } } }
Step 2: Initialize In-Memory Data
Create a class file named EmployeeData.cs within the Models folder, and then copy and paste the following code:
using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Data { public static class EmployeeData { public static List<Employee> Employees = new List<Employee> { new Employee { Id = 1, Name = "Alice Johnson", Gender = "Female", Department = "HR", City = "New York" }, new Employee { Id = 2, Name = "Bob Smith", Gender = "Male", Department = "IT", City = "Los Angeles" }, new Employee { Id = 3, Name = "Charlie Davis", Gender = "Male", Department = "Finance", City = "Chicago" }, new Employee { Id = 4, Name = "Sara Taylor", Gender = "Female", Department = "HR", City = "Los Angeles" }, new Employee { Id = 5, Name = "James Smith", Gender = "Male", Department = "IT", City = "Chicago" }, // Add more employees as needed }; } }
Example: Fetching Employee by ID using Route Data
For example, we have multiple employees and want to fetch one employee’s details by its ID. Then, how can we get the ID values? The URL is the best place to give us the ID value. So, we need to define one parameter to take the ID value within the method signature, as shown in the image below.
In ASP.NET Core Web API Application, if you want to pass anything as part of the URL Path (Route data), then we need to use curly braces {} and inside the curly braces, we need to give the same name as the method parameter name.
In our example, the GetEmployeeById method takes the id parameter, so we need to pass the id within the curly braces of the Route or HttpGet attribute, as shown in the image below. Here, we are using HttpGet, but you can also use Route Attribute.
Modifying Employee Controller:
So, please modify the EmployeeController class as shown below.
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/1 [HttpGet("{id}")] public ActionResult<Employee> GetEmployeeById(int id) { var employee = EmployeeData.Employees.FirstOrDefault(e => e.Id == id); if (employee == null) return NotFound($"Employee with ID {id} not found."); return Ok(employee); } } }
Code Explanation
- Route Attribute: [Route(“api/[controller]”)] sets the base route to api/Employee.
- Route Parameter: [HttpGet(“{id}”)] defines a GET endpoint with a dynamic {id} parameter.
- Action Method: GetEmployeeById retrieves the employee with the specified ID from the in-memory data.
Testing the Endpoint
Now, you can access the GetEmployeeById action method using the URL: /api/employee/1 (instead of 1, you can pass any dynamic value, i.e., the actual Employee ID, which information you want to retrieve). So run the above application, access the /api/Employee/100 endpoint, and you should get the output as shown in the image below.
Handling Multiple Route Parameters:
Sometimes, we might need to pass multiple dynamic values through the URL. For example, fetching employees based on both Gender and City. Let us understand how to pass multiple dynamic values using the Route attribute.
Example: Fetching Employees by Gender and City
Here, we want the Gender to be Male or Female, and city name. So, we will create one action method that will take two parameters, both are going to be of string type, as shown in the image below.
Now we want to access the above GetEmployeesByGenderAndCity action method using the URL: api/Employee/Gender/Male/City/Los Angeles
Here, Male and Los Angeles are the dynamic values. So, we need to decorate the GetEmployeesByGenderAndCity method with the Route or HttpGet Attribute, as shown in the image below. Here, we are passing the gender and city parameters in curly braces.
Modifying Employee Controller:
Please modify the EmployeeController class as shown below.
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/Gender/Male/City/Los Angeles [HttpGet("Gender/{gender}/City/{city}")] public ActionResult<IEnumerable<Employee>> GetEmployeesByGenderAndCity(string gender, string city) { var filteredEmployees = EmployeeData.Employees .Where(e => e.Gender.Equals(gender, StringComparison.OrdinalIgnoreCase) && e.City.Equals(city, StringComparison.OrdinalIgnoreCase)) .ToList(); if (!filteredEmployees.Any()) return NotFound($"No employees found with Gender '{gender}' in City '{city}'."); return Ok(filteredEmployees); } } }
Code Explanation
- Route Template: “Gender/{gender}/City/{city}” defines two dynamic route parameters: gender and city.
- Action Method: GetEmployeesByGenderAndCity filters employees based on the provided gender and city.
Testing the Endpoint
Now run the application and navigate to the URL /api/Employee/Gender/Male/City/Los Angeles, and you should get the message as expected, as shown in the image below.
Note: Passing resource identifiers as part of the Route Data is recommended. Resource identifier means the value which is going to identify a resource. Other optional values should be passed using the Query string parameters.
What Is a Query String?
Query Strings are key-value pairs appended to the URL after a question mark (?). Multiple query parameters are separated by &. They typically provide optional criteria or additional information for filtering, searching, or pagination details. In a typical RESTful API, they are not used to identify the main resource.
Use query strings for parameters that are not essential to identifying a resource but modify the response (e.g., filters, sorting). When passing multiple optional parameters, query strings provide a clean and flexible way to include them. You can specify as many optional query parameters as you want in any order.
Example: Searching Employees by Department using Query String
Let us understand the query string with an example. Now, we need to search employees by department. But we don’t want the department to be part of the Route Attribute. Then how can we do this? We can do this by using the query string parameter.
Modifying Employee Controller
Please modify the EmployeeController class as shown below. Here, we created a method called SearchEmployee with one parameter called Department. Further, notice that we have not included that parameter in the Route Attribute.
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/Search?Department=HR [HttpGet("Search")] public ActionResult<IEnumerable<Employee>> SearchEmployees([FromQuery] string department) { var filteredEmployees = EmployeeData.Employees .Where(e => e.Department.Equals(department, StringComparison.OrdinalIgnoreCase)) .ToList(); if (!filteredEmployees.Any()) return NotFound($"No employees found in Department '{department}'."); return Ok(filteredEmployees); } } }
Code Explanation
- Route Attribute: [HttpGet(“Search”)] sets the endpoint to api/Employee/Search.
- Query Parameter: [FromQuery] string department binds the department query string parameter to the action method’s parameter. Using the [FromQuery] attribute here is optional as, by default, value type parameters are bound from the Query string.
- Action Method: SearchEmployees filters employees based on the provided department.
Testing the Endpoint
The URL to access the above action method is api/Employee/Search, but how can we pass the value for the Department parameter? The answer is the query string. So, what we need to do is append ?Department=HR to the end of the URL (api/Employee/Search?Department=HR) and press enter button as shown in the image below.
Handling Multiple Query String Parameters in ASP.NET Core Web API
Let’s understand how to pass multiple query string parameters with an example. In real-world scenarios, search functionalities often require multiple optional parameters to filter data comprehensively.
Filtering Employees by City, Gender, and Department
Let’s say we want to filter employees by CITY, GENDER, AND DEPARTMENT. In that case, our action method should accept three parameters. So, modify the Employee Controller class as follows. If you want to make any query string parameter optional, you need to use ? or initialize the parameter with some default value.
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/Search?Gender=Male&Department=IT&City=Los Angeles [HttpGet("Search")] public ActionResult<IEnumerable<Employee>> SearchEmployees([FromQuery] string? gender, [FromQuery] string? department, [FromQuery] string? city) { var filteredEmployees = EmployeeData.Employees.AsQueryable(); if (!string.IsNullOrEmpty(gender)) filteredEmployees = filteredEmployees.Where(e => e.Gender.Equals(gender, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(department)) filteredEmployees = filteredEmployees.Where(e => e.Department.Equals(department, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(city)) filteredEmployees = filteredEmployees.Where(e => e.City.Equals(city, StringComparison.OrdinalIgnoreCase)); var result = filteredEmployees.ToList(); if (!result.Any()) return NotFound("No employees match the provided search criteria."); return Ok(result); } } }
Code Explanation?
- Optional Parameters: The ? in string? indicates that the parameters are optional.
- Dynamic Filtering: The action method dynamically applies filters based on the presence of query string parameters.
Testing the Endpoint
With the above changes in place, now run the application.
Filter by Gender Only:
Let’s say we want to filter the employees by Gender only. Then, you can only pass the Gender query string parameter in the URL (/api/Employee/Search?Gender=Male), as shown in the image below.
Filter by Gender and Department:
Now, if you want to search employees by Gender and Department, then you need to pass two query string parameters in the URL (/api/Employee/Search?Gender=Male&Department=IT), as shown in the image below.
Filter by All Parameters:
Now, you want to search the employees using all three parameters, i.e., Gender, Department, and City. then, you can pass all three query string parameters in the URL (/api/Employee/Search?Gender=Male&Department=IT&City=Los Angeles), as shown in the image below.
Note: The order of query string parameters does not affect the outcome.
Using Query Strings with Model Binding
Using Models to bind query string parameters can simplify the action methods, especially when dealing with multiple parameters. So, let us create a model called EmployeeSearch.cs within the Models folder and then copy and paste the following code.
namespace RoutingInASPNETCoreWebAPI.Models { public class EmployeeSearch { public string? Gender { get; set; } public string? Department { get; set; } public string? City { get; set; } } }
Modify the EmployeeController:
By default, the simple type parameters are mapped with query string and complex type parameters are mapped with the request body. However, in our example, we want to map the complex type parameter with the Query string values. So, in this case, we need to tell the ASP.NET Core Framework to map the complex type parameter with the query string parameter by using the FromQuery attribute. So, please modify the EmployeeController as follows:
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/Search?Gender=Male&Department=IT&City=Los Angeles [HttpGet("Search")] public ActionResult<IEnumerable<Employee>> SearchEmployees([FromQuery] EmployeeSearch searchCriteria) { var filteredEmployees = EmployeeData.Employees.AsQueryable(); if (!string.IsNullOrEmpty(searchCriteria.Gender)) filteredEmployees = filteredEmployees.Where(e => e.Gender.Equals(searchCriteria.Gender, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(searchCriteria.Department)) filteredEmployees = filteredEmployees.Where(e => e.Department.Equals(searchCriteria.Department, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(searchCriteria.City)) filteredEmployees = filteredEmployees.Where(e => e.City.Equals(searchCriteria.City, StringComparison.OrdinalIgnoreCase)); var result = filteredEmployees.ToList(); if (!result.Any()) return NotFound("No employees match the provided search criteria."); return Ok(result); } } }
Code Explanation
Model Binding: [FromQuery] EmployeeSearch searchCriteria binds all matching query string parameters to the EmployeeSearch model properties. This approach reduces the number of parameters in the action method, enhancing readability and maintainability.
Testing the Endpoint
Now, search the employees by Gender and Department as query strings in the URL (/api/Employee/Search?Gender=Female&Department=HR), and you should get the following result.
Accessing Query Strings Directly from HttpContext
In scenarios where you need more control or flexibility, you can access query string parameters directly from the HttpContext. For a better understanding, please modify the EmployeeController as follows. Here, we access the Gender, City, and Department query string parameter values directly from the action method using the HttpContext.Request.Query collection.
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/DirectSearch?Gender=Male&Department=IT [HttpGet("DirectSearch")] public ActionResult<IEnumerable<Employee>> DirectSearchEmployees() { var gender = HttpContext.Request.Query["Gender"].ToString(); var department = HttpContext.Request.Query["Department"].ToString(); var city = HttpContext.Request.Query["City"].ToString(); var filteredEmployees = EmployeeData.Employees.AsQueryable(); if (!string.IsNullOrEmpty(gender)) filteredEmployees = filteredEmployees.Where(e => e.Gender.Equals(gender, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(department)) filteredEmployees = filteredEmployees.Where(e => e.Department.Equals(department, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(city)) filteredEmployees = filteredEmployees.Where(e => e.City.Equals(city, StringComparison.OrdinalIgnoreCase)); var result = filteredEmployees.ToList(); if (!result.Any()) return NotFound("No employees match the provided search criteria."); return Ok(result); } } }
Code Explanation
The action method accesses query string parameters directly using HttpContext.Request.Query[“ParameterName”]. This approach allows the dynamic handling of query parameters without predefined bindings.
Testing the Endpoint
Now, with the above changes in place, run the application and search the employees by Gender and Department using query strings in the URL (/api/Employee/DirectSearch?Gender=Female&Department=HR), and you should get the following result.
Combining Route Parameters and Query Strings in ASP.NET Core Web API
ASP.NET Core Web API allows the combination of route parameters and query strings within the same endpoint, enabling more control over resource identification and additional filtering. Let’s consider an example where we have an API endpoint to get employee details with the following criteria:
- Route Parameter: Gender (e.g., Male, Female) identifies a subset of employees.
- Query Strings: Department and City provide additional filtering within the specified gender.
So, modify the Employee Controller as follows:
using Microsoft.AspNetCore.Mvc; using RoutingInASPNETCoreWebAPI.Data; using RoutingInASPNETCoreWebAPI.Models; namespace RoutingInASPNETCoreWebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { // GET api/Employee/Gender/Male?Department=IT&City=Los Angeles [HttpGet("Gender/{gender}")] public ActionResult<IEnumerable<Employee>> GetEmployeesByGender([FromRoute] string gender, [FromQuery] string? department, [FromQuery] string? city) { var filteredEmployees = EmployeeData.Employees .Where(e => e.Gender.Equals(gender, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(department)) filteredEmployees = filteredEmployees.Where(e => e.Department.Equals(department, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(city)) filteredEmployees = filteredEmployees.Where(e => e.City.Equals(city, StringComparison.OrdinalIgnoreCase)); var result = filteredEmployees.ToList(); if (!result.Any()) return NotFound("No employees match the provided search criteria."); return Ok(result); } } }
Code Explanation
- Route Parameter: {gender} is part of the route path, identifying the gender subset.
- Query Strings: department and city provide additional optional filtering.
- Action Method: GetEmployeesByGender handles both the route parameter and query strings.
Testing the Endpoint
Now, with the above changes in place, run the application and search the employees by Gender as Route Data, Department, and City as query strings in the URL (/api/Employee/Gender/Male?Department=IT&City=Los Angeles), and you should get the following result.
Query String vs Route Parameter in ASP.NET Core Web API
Understanding the differences between query strings and route parameters in ASP.NET Core Web API is essential for effective API design. Both are used to pass information to the server, but they serve different purposes and have different implications for API usability and functionality. The following are Key Differences
- Position in URL: Route parameters are part of the URL path, while query strings are appended to the URL.
- Optionality: Route parameters are generally mandatory, while query strings are optional.
- Use Cases: Route parameters identify specific resources (e.g., a particular product). Query strings are used for additional request information (e.g., filters, search terms).
Understanding and effectively utilizing Route Data and Query Strings are fundamental for building robust ASP.NET Core Web APIs. By appropriately distinguishing between mandatory and optional parameters and using the strengths of each approach, we can design APIs that are both user-friendly and maintainable.
This is how query strings work in ASP.NET Core Web API Routing. In the next article, I will discuss Setting Multiple URLs for a Single Resource in ASP.NET Core Web API Application using Routing. In this article, I explain how to work with Route Parameters and Query Strings in ASP.NET Core Web API Routing with Examples. I hope you enjoy Route Parameters and Query Strings in the ASP.NET Core Web API Routing article.
got clear idea about query string params and route data.
thanks sir