Passing parameters to actions is an essential part of building RESTful Web API. ASP.NET Core released as a part of .NET 5 offers multiple ways to pass parameters to methods, that represent your endpoints. Let’s see what they are.
Pass parameter as a part of an URL
When passing a parameter in a URL, you need to define a routing that would contain a parameter. Let’s have a look a the example:
[Route("{daysForward}")]
[HttpGet]
public IActionResult Get(int daysForward)
{
var rng = new Random();
return new JsonResult(new WeatherForecast
{
Date = DateTime.Now.AddDays(daysForward),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
});
}
This method returns a WeatherForecast
for a single day in the future. DaysForward
parameter represents how many days in advance weather forecast should be returned. Notice that daysForward
is a part of the routing, so a valid URL to this endpoint will look like:
GET: weatherForecast/3
We can also use [FromRoute]
attribute before variable type, but it will also work the same way by default.
public IActionResult Get([FromRoute] int daysForward)
Pass parameter in a query string
This is a very common method for passing additional parameters, because it does not require us to change routing, so it is also backward compatible. It’s important if we were to change an existing solution.
Let’s look at a different method, that would return a collection of weather forecasts with a sorting option.
[HttpGet]
public IEnumerable<WeatherForecast> Get([FromQuery]bool sortByTemperature = false)
{
var rng = new Random();
var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
});
if (sortByTemperature)
{
forecasts = forecasts.OrderByDescending(f => f.TemperatureC);
}
return forecasts;
}
In this example, we pass on sortByTemperature
parameter which is optional. Notice that we use [FromQuery]
attribute to indicate, that it’s a variable taken from the query string. A URL to this endpoint would look like this:
GET: weatherForecast?sortByTemperature=true
You can put many parameters like this:
GET: weatherForecast?key1=value1&key2=value2&key3=value3
Remember, that URL needs to be encoded properly to work right. If you were to pass a parameter like this:
https://www.michalbialecki.com/?name=Michał Białecki
It will need to be encoded into:
https://www.michalbialecki.com/?name=Micha%C5%82%20Bia%C5%82ecki
Pass an object in a query string
When you’re passing a lot of query parameters, it might be worth to handle them as an object. Take a look at the code below:
// GET: weatherForecast/GetFiltered?SortByTemperature=true&City=Poznan
[HttpGet("GetFiltered")]
public IEnumerable<WeatherForecast> GetFiltered([FromQuery]WeatherForecastFilters filters)
{
var rng = new Random();
var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)],
City = filters.City
});
if (filters.SortByTemperature)
{
forecasts = forecasts.OrderByDescending(f => f.TemperatureC);
}
return forecasts;
}
As you can see, I’m passing WeatherForecastFilters
class as a query parameter. Notice that I’m using [FromQuery]
attribute before the class name.
And WeatherForecastFilters
class looks like this:
public class WeatherForecastFilters
{
public bool SortByTemperature { get; set; }
public string City { get; set; }
}
It is a standard class, without any additional attributes. Let’s now make a request to the URL below:
GET: /WeatherForecast/GetFiltered?SortByTemperature=true&City=Poznan
The response would be:
So parameters can be passed-in by their names, even if they are inside the class. What’s more, they are accessible just by their names, without any class prefix and that means that all of the properties must have unique names.
Here is a screenshot from Visual Studio.
Handling query parameters as class properties will work, but only inside this class. It will not work with the nested objects – their properties will not be mapped.
Pass parameters with headers
Passing parameters in a request headers are less popular, but also widely used. It doesn’t show in a URL, so it’s less noticeable by the user. A common scenario for passing parameters in a header would be providing credentials or a parent request id to enable multi-application tracking. Let’s have a look at this example:
[HttpPost]
public IActionResult Post([FromHeader] string parentRequestId)
{
Console.WriteLine($"Got a header with parentRequestId: {parentRequestId}!");
return new AcceptedResult();
}
In order to send a POST request, we would need to use some kind of a tool, I’ll use Postman:
Here you see that I specified headers and parentRequestId
is one of them.
Pass parameters in a request body
The most common way to pass the data is to include it in a request body. We can add a header Content-Type
with value application/json
and inform the receiver how to interpret this body. Let’s have a look at our example:
[HttpPost]
public IActionResult Post([FromBody] WeatherForecast forecast)
{
Console.WriteLine($"Got a forecast for data: {forecast.Date}!");
return new AcceptedResult();
}
We use [FromBody]
attribute to indicate, that forecast will be taken from request body. In ASP.NET Core for .NET 5 we don’t need and serialize to deserialize json body to WeatherForecast
object, it will work automatically. To send POST request, let’s use Postman once again:
Have in mind, that size of the request body is limited by the server. It can be anywhere between 1MB to 2GB. In ASP.NET Core 5 maximum request body size is around 28MB, but that can be changed. What if I would like to send bigger files than that, over 2GB? Then you should look into sending content as a stream or sending it in chunks.
Pass parameters in a form
Sending content in a form is not very common, but it is the best solution if you want to upload a file. Let’s have a look at the example:
[HttpPost]
public IActionResult SaveFile([FromForm] string fileName, [FromForm] IFormFile file)
{
Console.WriteLine($"Got a file with name: {fileName} and size: {file.Length}");
return new AcceptedResult();
}
This method doesn’t really send a file, but it will successfully receive a file from the request. The interface IFormFile
is used specifically for handling a file.
When sending a request we need to set Content-Type
to application/x-www-form-urlencoded
and it the Body part, we need to choose a file:
Let’s see what do we get when we debug this code:
And the file is correctly read. An interesting fact is, that with IFormFile
we get not only binary data but also a file type and name. So you might ask why I send a file name separately? This is because you might want to name file differently on the server, then the one you are sending.
Hope you enjoyed this post, you can have a look at the code posted here on my Github:
https://github.com/mikuam/PrimeHotel