In this article, we’ll discuss the different ways to pass parameters to a GET request in ASP.NET Core.

To download the source code for this article, you can visit our GitHub repository.

Before we dive into this topic, we recommend going through the basics of handling the GET request and best practices for ASP.NET Core Web API.

When working with ASP.NET Core, we use GET methods to retrieve data from a web API. We often must pass parameters to a GET method to specify which data to retrieve.

There are several ways we can pass parameters to a method:

  • Query parameters
  • Route parameters
  • Combination of query and route parameters
  • Request body
  • Header Parameters

Let’s take a closer look at each section to gain a better understanding.

Don't like the ads? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!

Setting Up the Application

To start with, we’ll create two model classes, Product:

public class Product
{
    public int Id { get; set; }
    public string? Category { get; set; }
    public string? Brand { get; set; }
    public string? Name { get; set; }
    public int WarrantyYears { get; set; }
    public bool IsAvailable { get; set; }
}

And ProductDto:

public class ProductDto
{
    public int Id { get; set; }
    public string? Category { get; set; }
    public string? Brand { get; set; }
    public string? Name { get; set; }
    public int WarrantyYears { get; set; }
    public bool IsAvailable { get; set; }
}

Then, we will create a Map class to map the data from the Product class to the ProductDto class:

public class Map : Profile
{
    public Map()
    {
        CreateMap<Product, ProductDto>();
    }
}

To better understand how AutoMapper works, we suggest reading our guide on using AutoMapper in ASP.NET Core.

Now, let’s create a ProductController class and define a list of¬†products:

[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
    private readonly List<Product> _products = new()
    {
        new Product{ Id = 1, Category = "Electronic", Brand = "Sony", 
                     Name = "Play Station", WarrantyYears = 2, IsAvailable = true },
        //removed other entires for a better readability
        new Product{ Id = 7, Category = "Electronic", Brand = "Apple", 
                     Name = "Mobile", WarrantyYears = 2, IsAvailable = true }
    };
}

Finally, let’s declare a¬†field named mapper of type IMapper, and inject an instance of IMapper in the constructor:

private readonly IMapper _mapper;

public ProductController(IMapper mapper)
{
    _mapper = mapper;
}

Now that we have finished the initial steps, let’s explore each section in detail.

Query Parameters

In a GET method, we commonly use query strings to pass parameters. We include them in the URL as key-value pairs separated by an equal sign and separate multiple parameters by an ampersand symbol.

Query parameters begin with a question mark (?), and we put them after the URL. If any route parameters exist in the URL, query parameters should be placed after them.

In this URL, category and brand are the query parameters with the values “Electronic” and “Sony”:

https://localhost:7097/api/Product?category=Electronic&brand=Sony

When we work with query parameters, we have the option to use the [FromQuery] attribute to bind parameters from the query string explicitly. However, the parameter binding process automatically converts request data into strongly typed parameters. This process eliminates the need for explicit [FromQuery] attribute usage.

First, let’s create a GET method without using the [FromQuery] attribute:

[HttpGet]
public IActionResult GetProductsByCategoryAndBrand(string category, string brand)
{
    List<Product> result = _products
        .Where(x => x.Category == category && x.Brand == brand)
        .ToList();

    return Ok(_mapper.Map<List<ProductDto>>(result));
}

We have a GET endpoint that accepts two parameters category and brand. Then, we use the LINQ Where method to filter the _products based on the category and brand and map the result with the ProductDto class.

Let’s proceed by sending a request to the endpoint:

Pass two query prameters with a Get request in Postman

We can observe that parameter names in our method signature align with the query parameter names in the request URL, so the framework automatically binds the values from the query string to the method parameters based on their names and filters the _products.

Sometimes, we may encounter situations where the parameter names in the method signature do not match the query parameter names. In such scenarios, we can explicitly use the [FromQuery] attribute to bind the query parameters to the respective method parameters.

Now, let’s create a GET method that takes two parameters from the query string, and we use the FromQuery attribute:

[HttpGet("type-manufacturer")]
public IActionResult GetProductsByCategoryAndBrandUsingFromQuery(
    [FromQuery(Name = "type")] string category,
    [FromQuery(Name = "manufacturer")] string brand)
{
    List<Product> result = _products
        .Where(x => x.Category == category && x.Brand == brand)
        .ToList();

    return Ok(_mapper.Map<List<ProductDto>>(result));
}

We use [FromQuery(Name = "type")] and [FromQuery(Name = "manufacturer")] attribute to bind the values of query parameters type and manufacturer to the corresponding method parameters category and brand.

To test this, let’s send a request with two query parameters:

Postman Result showing the API request and response.

The API filters the data and returns the result based on the query parameters type and manufacturer.

Since there is a limit to the length of a URL, query parameters have a limited capacity for passing data. Check out this helpful resource for information on the maximum character limit for a query string in GET requests for different browsers.

Additionally, query parameters are not secure because the values are visible in the URL, making them vulnerable to interception by malicious users. 

Route Parameters

We can also use route parameters to pass data but in a different way. Route parameters are placeholders in the URL. We use route parameters to identify a specific resource or resources, while the query parameters to sort/filter those resources.

We recognize route parameters by curly braces ({}) containing a parameter name as part of the route template.

Let’s take a look at an URL that contains the route parameter:

https://localhost:7097/api/Product/2

We use this URL to access a product resource by passing the product Id as a route parameter to the API endpoint.

By default, the framework binds route parameters based on their names when we use route parameters, allowing us to omit the [FromRoute] attribute. However, there are scenarios where the parameter names in the method signature differ from the route parameter names. In such situations, we can use the [FromRoute] attribute.

Let’s create another GET method without using FromRoute attribute:

[HttpGet("{id}")]
public IActionResult GetProductById(int id)
{
    return Ok(_mapper.Map<ProductDto>(_products
        .Where(x => x.Id == id)
        .FirstOrDefault()));
}

Firstly, we define a GET method with a route parameter “id” using the [HttpGet("{id}")] attribute. Then, using the LINQ query, we filter the _products and map the retrieved product to a ProductDto.

Let’s send a request with a route parameter:

Pass route parameters with a Get request in Postman

The API utilizes the route parameter to identify the specific resource.

Let’s create an additional GET method that uses [FromRoute] attribute:

[HttpGet("productId/{productId}")]
public IActionResult GetProductByIdUsingFromRoute([FromRoute(Name = "productId")] int id)
{
    return Ok(_mapper.Map<ProductDto>(_products
        .Where(x => x.Id == id)
        .FirstOrDefault()));
}

In this GET method, we define a [FromRoute(Name = "productId")] attribute to bind the productId parameter to the corresponding action method parameter.

Let’s send a request to the endpoint:

Postman Result showing the API request and response.

As we can see, API identifies the resource based on the route parameter we passed.

Combination of Query and Route Parameters

We can combine query and route params to pass params in a GET method. It is often more appropriate to pass a set of required parameters as route parameters and optional parameters as query parameters.

With this approach, we can design an API endpoint to include some of the parameters in the URL as route parameters while passing others in the URL as query parameters. The route parameters help identify the requested primary resource, while the query parameters help filter the result based on additional criteria.

Now, we’ll create a GET method that uses both query and route params::

[HttpGet("brand/{brand}")]
public IActionResult GetProductsByBrandAndWarranty(string brand, int warranty)
{
    return Ok(_mapper.Map<List<ProductDto>>(_products
        .Where(x => x.Brand == brand && x.WarrantyYears == warranty)
        .ToList()));
}

The brand parameter is included in the URL as a route parameter while we pass the warranty as a query parameter. This allows retrieving _products by the brand while further filtering the result by the warranty.

We’ll send a request to the endpoint:

Postman Result showing the API request and response.

The API filters the _products based on the route and query parameters.

Now, let’s create a new GET action method that uses both the [FromQuery] and [FromRoute] attributes:

[HttpGet("manufacturer/{manufacturer}")]
public IActionResult GetProductsByBrandAndWarrantyUsingAttributes(
    [FromRoute(Name = "manufacturer")] string brand,
    [FromQuery(Name = "coverage")] int warranty)
{
    return Ok(_mapper.Map<List<ProductDto>>(_products
        .Where(x => x.Brand == brand && x.WarrantyYears == warranty)
        .ToList()));
}

We have a GET endpoint that retrieves products based on the brand and warranty attributes. We use the [FromRoute(Name = "manufacturer")] attribute to ensure that the value for the brand parameter is retrieved from the route segment. Meanwhile, the [FromQuery(Name = "coverage")] attribute indicates that the warranty value will be obtained from the query string.

Let’s send a request:

Pass parameters with a Get request using Query and Route Parameters

By utilizing both the brand route parameter and the coverage query parameter, the API filters the _products.

Request Body

We typically use the request body for more complex or sensitive data that we cannot easily pass in the URL. It is commonly used in HTTP POST, PUT, and PATCH methods to send data to the server. According to the best practices of REST API, we don’t recommend passing parameters through the request body in a GET method, although it is possible.

We encapsulate the parameters in a model or class to use the request body to pass multiple parameters to a GET method. The properties of the model should correspond to the parameters we pass.

By default, the HTTP methods GET, HEAD, OPTIONS, and DELETE do not bind data implicitly from the request body. To bind data explicitly from the request body, marked as JSON, for these methods, we can use the [FromBody] attribute. Also, we can use the [FromQuery] attribute as we did in our Pagination article.

For this example, we are going to create another GET method that accepts the request body using [FromBody] attribute:

[HttpGet("category")]
public IActionResult GetProductsByCategory([FromBody] ProductDto model)
{
    return Ok(_mapper.Map<List<ProductDto>>(_products
        .Where(x => x.Category == model.Category)
        .ToList()));
}

We use the route name “category” to indicate that we want to retrieve a list of _products belonging to a specific category. We utilize the ProductDto class to pass the model into the request body. The [FromBody] attribute binds the request body values to the specified model.

Furthermore, we use LINQ to filter the _products based on their category and map the resulting _products to the ProductDto class.

Passing data in a request body is more secure than exposing the values in the URL.

Let’s send a request to the endpoint:

Postman Result showing the API request and response.

The API filters the result based on the category parameter passed in the request body in JSON format.

Header Parameters

Header parameters are another way to pass data to a GET method using [FromHeader] attribute. Unlike query and route parameters, header parameters are not part of the URL. Instead, we can send them as part of the request headers.

Header parameters are an essential aspect of HTTP requests that provide additional information about the request. We can use header parameters to convey information such as authentication tokens, content type, and content-encoding.

Now, we’ll create a GET method that uses the header parameters:

[HttpGet("category-brand")]
public IActionResult GetProductsByCategoryAndBrandViaHeaders([FromHeader] string category, [FromHeader] string brand)
{
    return Ok(_mapper.Map<List<ProductDto>>(_products
        .Where(x => x.Category == category && x.Brand == brand)
        .ToList()));
}

We use the [FromHeader] attribute to bind the category and brand header parameter values to the corresponding method parameters. We use “category-brand” as the route name because it filters the _products based on the provided category and brand. Then using the header parameters, we filter the _products using the LINQ Where() method, map them to ProductDto and return them.

Using header parameters in GET requests is an excellent option to pass sensitive parameter values to avoid exposing them in the URL.

Let’s go ahead and send a request using the header parameters:

Postman Result when we pass parameters with a GET reqeust

When we send the API request, we pass the category and brand parameters in the headers, which the API uses to filter the results and return the _products.

Conclusion

In this article, we discussed various ways of passing parameters to a GET method.

We commonly use query and route parameters to pass parameters in GET requests. However, if the parameter values are sensitive, header parameters or the request body may be more appropriate. We recommend avoiding passing data in the request body for GET requests and only considering it if other options are not feasible.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!