Very possible this is a duplicate, but I've looked and can't find an answer. The first answer here looked promising: Query string not working while using attribute routing But I tried that and it didn't work.
[HttpGet, Route("api/machine/byid/{id=id}/{pageNumber=pageNumber}/{pageSize=pageSize}/{fields=fields}")]
public string ById(int id, int pageNumber, int pageSize, string fields)
// code removed
}
This works:
https://localhost:44303/api/machine/byid/1/2/3/a,b,c
This does not:
https://localhost:44303/api/machine/byid?id=1&pageNumber=2&pageSize=3&fields=a,b,c
The second url returns:
{"type":"https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|bf12950b-472923d3a24062d1.","errors":{"id":["The value 'id' is not valid."],"pageSize":["The value 'pageSize' is not valid."],"pageNumber":["The value 'pageNumber' is not valid."]}}
You would need two routes:
[HttpGet("api/machine/byid")]
public string ById(
[FromQuery("id")] int id,
[FromQuery("pageNumber")] int pageNumber,
[FromQuery("pageSize")] int pageSize,
[FromQuery("fields")] string fields)
{
}
Follow this link for more informations
The example you provided demonstrates route parameters. There is a distinct difference between route parameters and query parameters.
To accomplish query parameters, you can the [FromQuery] attribute to your method parameters. This will allow for the query parameter example that you provided,
Example : https://localhost:5000/api/persons?firstName=bob&lastName=smith
You can also provide default values for these from within your method parameters. You can string multiple query parameters together in one action.
For route parameters, the parameters are provided via the route itself.
Example : https://localhost:5000/api/persons/23
These parameters are defined from within the [HttpGet("{id}")] attribute on your controller action. You can also constrain the parameter to a certain type, such as an int. This is achieved by adding a colon and specifying the type. Example [HttpGet("{id:int}")]. No further attributes are required to be added within your method parameters for route parameters.
Of course you must also declare these parameters in your method parameters, for both types.
// "/api/persons/23"
[HttpGet("{id}")]
public async Task<IActionResult> GetPersonById(int id)
{
// Code ...
}
// "/api/persons?firstName=bob&lastName=smith"
[HttpGet]
public async Task<IActionResult> GetPersonByName([FromQuery] string firstName = null, [FromQuery] string lastName = null)
{
// Code here... both firstName and lastName can now be optional or only one provided
}
The answer by sturcotte06 was close, but was not 100% Core compliant. This works:
[HttpGet, Route("api/machine/byid/{id=id}/{pageNumber=pageNumber}/{pageSize=pageSize}/{fields=fields}")]
public string ById([FromQuery] int id, [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string fields)
{
// code removed
}
Related
Consider this example:
[HttpGet]
[Route("[action]/{id}")
public IActionResult Foo([FromRoute] int id) {
//etc.
}
I can avoid magic strings for areas, controllers, actions by using the special tokens [controller], [action], etc.
This is a simple action with a single int argument. Is it also possible somehow to avoid magic string {id}?
For what reason should we apply these attributes in ASP.NET Core model binding?
What are the consequences of not using them?
Isn't the model binding engine able to search through the incoming request and map them to Controller action method parameters without these attributes:
[FromQuery] - Gets values from the query string.
[FromRoute] - Gets values from route data.
[FromForm] - Gets values from posted form fields.
[FromBody] - Gets values from the request body.
[FromHeader] - Gets values from HTTP headers.
See this Controller action method examples:
public ActionResult<Pet> Create([FromBody] Pet pet)
public ActionResult<List<Pet>> Search([FromRoute] string breed, [FromQuery] string color, [FromQuery] int age)
We can also apply the attributes to the model class:
public class Pet
{
public string Name { get; set; }
[FromQuery]
public string Breed { get; set; }
}
Source: Microsoft Docs
Controller action method examples without attributes:
public ActionResult<Pet> Create(Pet pet)
public ActionResult<List<Pet>> Search(string breed, string color, int age)
You could check the Sources description:
By default, model binding gets data in the form of key-value pairs from the following sources in an HTTP request:
Form fields
The request body (For controllers that have the [ApiController] attribute.)
Route data
Query string parameters
Uploaded files
For each target parameter or property, the sources are scanned in the order indicated in the preceding list. If the default source is not correct, we can use one of the following attributes to specify the source:
[FromQuery] - Gets values from the query string.
[FromRoute] - Gets values from route data.
[FromForm] - Gets values from posted form fields.
[FromBody] - Gets values from the request body.
[FromHeader] - Gets values from HTTP headers.
For example:
When using the following method, it will get the pet data from the form fields:
public ActionResult<Pet> Create(Pet pet)
If using the following method, it will get the parameter from the default source. We can pass the parameter via the Form or Query string.
public ActionResult<List<Pet>> Search(string breed, string color, int age)
If adding attribute to above method, like this:
public ActionResult<List<Pet>> Search([FromQuery]string breed, [FromQuery]string color, [FromQuery]int age)
You could only pass the parameter via the Query string. In this scenario, if you pass the parameters via Form, the parameters in the action method will be Null.
So, by using these attributes we could specify the model binding source, without to scan the default source list.
I am using [FromQuery] atribute in controller's Get method:
//CarsController, etc..
[HttpGet]
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery] CarsParameter? carsParam = null)
{
//param is always not null here
}
Inside the method I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null, so I cannot say if the Color="" (defailt value) is intended to be empty string or it's because of the call was api/cars
CarsParameter is a simple class:
public class CarsParameter
{
public string Color {get; set;} = "";
//more params here
}
Yes, I can use different path, like api/cars/withParams?color=red, but i am looking for more subtle solution.
I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null
Please note that default model binding starts by looking through the sources for the key carsParam.Color. If that isn't found, it looks for Color without a prefix, which cause the issue.
To achieve your requirement, you can try to specify prefix explicitly, like below.
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery][Bind(Prefix = "carsParam")] CarsParameter? carsParam = null)
{
Request to api/cars?color=red&carsParam.color=yellow&carsParam.brand=test and following is test result
I would like to know how we can design .net core api controller to accept an array value like given below
http://localhost:32656/api/Values?str[]="abc"&str[]="xyz"
I did some research online and the only two options, I was able to find is either I need to pass indexes inside the array
eg:- http://localhost:32656/api/Values?str[0]="abc"&str[1]="xyz" (Pass indexes inside the array)
or I need to pass the array as repeated query strings.
eg:- http://localhost:32656/api/Values?str="abc"&str="xyz" (Pass it as repeated query strings)
But I would like to see the other possible options to send an array to .net core 2.1 api controller.
You can take advantage of the FromQuery attribute here, specifying the Name property as str[]:
public IActionResult Values([FromQuery(Name = "str[]")] List<string> strValues)
{
...
}
If you also want to strip out the "s for each value, you can use a Select. Here's a an example:
public IActionResult Values([FromQuery(Name = "str[]")] List<string> strValues)
{
var strValuesWithoutQuotes = strValues.Select(x => x.Trim('"'));
...
}
Here is how we do it:
[Route("api/v1/[controller]")]
public class TestController : Controller
{
[HttpGet]
public async Task Get(List<string> stringValues)
{
...
}
}
Then call the endpoint with http://localhost/api/v1/test?stringValues=string1&stringValues=string2
stringValues should have the list of values in the query string
I am creating an API that has a controller with one GET action:
[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(int param1, int param2) {
// method body...
}
The URLs will be in the following format:
/api/xxxxx/1/1?p1=5&p2=hello&p3=20161108
/api/xxxxx/1/1?p1=active
The number and names of the of query string parameters will vary.
I want to pass the query string parameters into the controller method, but I cannot hard code them into the method signature, due to the varying names and numbers. Is there a way to do this? I've tried calling var qsParams = ControllerContext.Request.GetQueryNameValuePairs();, but I get a resource not found error when trying to request any URL with a query string, given the Route attribute shown above.
I've come up with one alternative: use route values instead of query string parameters, then use the catch-all {*tags} and pass it as a method parameter:
[Route("api/xxxxx/{param1:int}/{param1:int}/{*tags}")]
public IHttpActionResult Get(int param1, int param2, string tags) {
// method body...
}
With URLs in the format
/api/xxxxx/1/1/5/john/20161108
/api/xxxxx/1/1/active
This works, but I'd rather use the query string to be able to use named keys instead of relying on the ordering of the parameters (also, using the query string seems to better conceptual match for what I'm doing).
So, how can I pass variable query string parameters into a controller action? I say "pass" the parameters, but they don't necessarily need to be passed as a method parameters, as long as I could access the query string parameters from the method body, while getting a URL with a query string to resolve to the action in question.
EDIT:
It's worth mentioning that creating multiple action methods for every possible parameter set is not an option.
EDIT 2:
I see two direct solutions, if they're possible:
Pass the entire query string to the action method as a single string
parameter. I could then manually parse the query string.
Be able to use ControllerContext.Request.GetQueryNameValuePairs()
inside the method body, while NOT adding corresponding parameters to
the method signature.
However, I haven't figured out if these two things are possible, though it seems likely one or both would be.
If you want to send an Entity to your Controller you can get it by [FromBody]
Think an Entity is the simplest way to pass it.
F.e.
[HttpPost]
[Route("api/xxxxx/send")]
public void SendReuqest([FromBody] Entity name)
hope understand your problem right.
I'm posting an answer to document alternatives I've come up with. I know some (all?) of these will be hacky. Nonetheless...
ANSWER
Use method GetQueryNameValuePairs. This is the very first thing I tried, but I must have had something different in the route attribute or the method signature, because I was getting a "not found" error before. Now, however, this is working perfectly. This makes this whole question and answer basically moot for me.
Example URL:
/api/xxxxx/1/1?p1=5&p2=john&p3=20161108
Action:
[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(int param1, int param2) {
var qsParams = ControllerContext.Request.GetQueryNameValuePairs();
// rest of method body...
}
Option 1
Use a single query string parameter with a custom format that the controller understands and will be able to parse.
Example URL:
/api/xxxxx/1/1?qs=p1~5|p2~john|p3~20161108
Action:
[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(int param1, int param2, string qs) {
string[] qsParamsArray = qs.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
IDictionary<string, string> qsParams = new Dictionary<string, string>();
foreach (string p in qsParamsArray) {
string[] kv = p.Split(new string[] { "~" }, StringSplitOptions.None);
if (String.IsNullOrWhiteSpace(kv[0])) {
return NotFound();
}
qsParams.Add(kv[0], kv[1]);
}
// Use qsParams as desired...
}
Option 2
Put the query string parameters in the route instead of the query string. Must rely on ordering of parameters.
Example URL:
/api/xxxxx/1/1/5/john/20161108
Action:
[Route("api/xxxxx/{param1:int}/{param1:int}/{*tags}")]
public IHttpActionResult Get(int param1, int param2, string tags) {
// parse the "tags" parameter here...
// rest of method body...
}
Option 3
Hard code a maximum number of optional parameters with generic names, and specific query string parameters for both names and values of the parameters.
Example URL:
/api/xxxxx/1/1?name1=p1&value1=5&name2=p2&value2=john&name3=p3&value3=20161108
Action:
[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(
int param1,
int param2,
string name1 = null,
string value1 = null,
string name2 = null,
string value2 = null,
string name3 = null,
string value3 = null,
string name4 = null,
string value4 = null,
...,
string nameNMAX = null,
string valueNMAX = null
) {
// method body...
}