Bind multiple values from the query string to an array parameter - c#

I have created the following ActionMethod as suggested here:
[Route("[action]")]
public IActionResult Orders([FromRoute] int[] filterby)
{
ICollection<Order> all;
if(filterby.Length != 0)
{
all = _db.Orders.Where(s => filterby.Contains((int)s.Status)).ToList();
}
else
{
all = _db.Orders.ToList();
}
return View(all);
}
When the request URI is something like:
http://localhost:5000/admin/orders?filterby=0&filterby=1
The filterby parameter is always an array of 0 length. What can't I get it working?

When using ASP.NET core you have two choices:
(Recommended) Use the new attribute [FromQuery]
Import the package Microsoft.AspNet.Mvc.WebApiCompatShim and use the old [FromUri]

Related

Unable to return anonymous object through an IActionResult Endpoint

I have a problem when I try to return an anonymous object/DTO from an endpoint in my ASP .NET Core 6 Web-Api Project. I get an error that it cannot implicitly convert the type to an IActionResult. The code I use was suggested in this answer but unfortunately it doesn't work out for me, I also tried using to map the anonymous object to an dto but that didn't work either. Any help would be greatly appreciated, thanks in advance.
I also tried to return an Json(output) like suggested in the answer but, Json() I can't specify a using for "Json", I also tried to return an JsonResult(output) by modifying the return type of the endpoint but that didn't work either.
This is the code I use:
[HttpGet]
public IActionResult GetAllEndpoints()
{
var endpoints = _endpoinDataSources
.SelectMany(es => es.Endpoints)
.OfType<RouteEndpoint>();
var output = endpoints.Select(
e =>
{
var controller = e.Metadata
.OfType<ControllerActionDescriptor>()
.FirstOrDefault();
var action = controller != null
? $"{controller.ControllerName}.{controller.ActionName}"
: null;
var controllerMethod = controller != null
? $"{controller.ControllerTypeInfo.FullName}:{controller.MethodInfo.Name}"
: null;
return new GetEndpointsDto
{
Method = e.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault()?.HttpMethods?[0],
Route = $"/{e.RoutePattern.RawText.TrimStart('/')}",
Action = action,
ControllerMethod = controllerMethod
};
}
);
return output;
}
I also injected the EndPointDataSource through the controller constructor:
public MyController(IEnumerable<EndpointDataSource> endpointSources)
{
_endpoinDataSources = endpointSources;
}
You need to be more specific with return type:
[HttpGet]
public IActionResult<List<GetEndpointsDto>> GetAllEndpoints()
{
...
return Ok(output.toList())
}
You should try public ActionResult<object> GetAllEndpoints() and then return something like return Ok(output) or also just return output I realized that I'm returning output in a API of mine and it works fine

Howto read URI Params from querystring ASPNET 5 [duplicate]

I'm building one RESTful API using ASP.NET Core MVC and I want to use querystring parameters to specify filtering and paging on a resource that returns a collection.
In that case, I need to read the values passed in the querystring to filter and select the results to return.
I've already found out that inside the controller Get action accessing HttpContext.Request.Query returns one IQueryCollection.
The problem is that I don't know how it is used to retrieve the values. In truth, I thought the way to do was by using, for example
string page = HttpContext.Request.Query["page"]
The problem is that HttpContext.Request.Query["page"] doesn't return a string, but a StringValues.
Anyway, how does one use the IQueryCollection to actually read the querystring values?
You can use [FromQuery] to bind a particular model to the querystring:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding
e.g.
[HttpGet()]
public IActionResult Get([FromQuery(Name = "page")] string page)
{...}
You could use the ToString method on IQueryCollection which will return the desired value if a single page parameter is specified:
string page = HttpContext.Request.Query["page"].ToString();
if there are multiple values like ?page=1&page=2 then the result of the ToString call will be 1,2
But as #mike-g suggested in his answer you would better use model binding and not directly accessing the HttpContext.Request.Query object.
ASP.NET Core will automatically bind form values, route values and query strings by name. This means you can simply do this:
[HttpGet()]
public IActionResult Get(int page)
{ ... }
MVC will try to bind request data to the action parameters by name ... below is a list of the data sources in the order that model binding looks through them
Form values: These are form values that go in the HTTP request using the POST method. (including jQuery POST requests).
Route values: The set of route values provided by Routing
Query strings: The query string part of the URI.
Source: Model Binding in ASP.NET Core
FYI, you can also combine the automatic and explicit approaches:
[HttpGet()]
public IActionResult Get(int page
, [FromQuery(Name = "page-size")] int pageSize)
{ ... }
Here is a code sample I've used (with a .NET Core view):
#{
Microsoft.Extensions.Primitives.StringValues queryVal;
if (Context.Request.Query.TryGetValue("yourKey", out queryVal) &&
queryVal.FirstOrDefault() == "yourValue")
{
}
}
You can just create an object like this:
public class SomeQuery
{
public string SomeParameter { get; set; }
public int? SomeParameter2 { get; set; }
}
And then in controller just make something like that:
[HttpGet]
public IActionResult FindSomething([FromQuery] SomeQuery query)
{
// Your implementation goes here..
}
Even better, you can create API model from:
[HttpGet]
public IActionResult GetSomething([FromRoute] int someId, [FromQuery] SomeQuery query)
to:
[HttpGet]
public IActionResult GetSomething(ApiModel model)
public class ApiModel
{
[FromRoute]
public int SomeId { get; set; }
[FromQuery]
public string SomeParameter { get; set; }
[FromQuery]
public int? SomeParameter2 { get; set; }
}
StringValues is an array of strings. You can get your string value by providing an index, e.g. HttpContext.Request.Query["page"][0].
IQueryCollection has a TryGetValue() on it that returns a value with the given key. So, if you had a query parameter called someInt, you could use it like so:
var queryString = httpContext.Request.Query;
StringValues someInt;
queryString.TryGetValue("someInt", out someInt);
var daRealInt = int.Parse(someInt);
Notice that unless you have multiple parameters of the same name, the StringValues type is not an issue.
in .net core if you want to access querystring in our view use it like
#Context.Request.Query["yourKey"]
if we are in location where #Context is not avilable we can inject it like
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#if (HttpContextAccessor.HttpContext.Request.Query.Keys.Contains("yourKey"))
{
<text>do something </text>
}
also for cookies
HttpContextAccessor.HttpContext.Request.Cookies["DeniedActions"]
Maybe it helps.
For get query string parameter in view
View:
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#{ Context.Request.Query["uid"]}
Startup.cs ConfigureServices :
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
I have a better solution for this problem,
request is a member of abstract class ControllerBase
GetSearchParams() is an extension method created in bellow helper
class.
var searchparams = await Request.GetSearchParams();
I have created a static class with few extension methods
public static class HttpRequestExtension
{
public static async Task<SearchParams> GetSearchParams(this HttpRequest request)
{
var parameters = await request.TupledParameters();
try
{
for (var i = 0; i < parameters.Count; i++)
{
if (parameters[i].Item1 == "_count" && parameters[i].Item2 == "0")
{
parameters[i] = new Tuple<string, string>("_summary", "count");
}
}
var searchCommand = SearchParams.FromUriParamList(parameters);
return searchCommand;
}
catch (FormatException formatException)
{
throw new FhirException(formatException.Message, OperationOutcome.IssueType.Invalid, OperationOutcome.IssueSeverity.Fatal, HttpStatusCode.BadRequest);
}
}
public static async Task<List<Tuple<string, string>>> TupledParameters(this HttpRequest request)
{
var list = new List<Tuple<string, string>>();
var query = request.Query;
foreach (var pair in query)
{
list.Add(new Tuple<string, string>(pair.Key, pair.Value));
}
if (!request.HasFormContentType)
{
return list;
}
var getContent = await request.ReadFormAsync();
if (getContent == null)
{
return list;
}
foreach (var key in getContent.Keys)
{
if (!getContent.TryGetValue(key, out StringValues values))
{
continue;
}
foreach (var value in values)
{
list.Add(new Tuple<string, string>(key, value));
}
}
return list;
}
}
in this way you can easily access all your search parameters. I hope this will help many developers :)
Some of the comments mention this as well, but asp net core does all this work for you.
If you have a query string that matches the name it will be available in the controller.
https://myapi/some-endpoint/123?someQueryString=YayThisWorks
[HttpPost]
[Route("some-endpoint/{someValue}")]
public IActionResult SomeEndpointMethod(int someValue, string someQueryString)
{
Debug.WriteLine(someValue);
Debug.WriteLine(someQueryString);
return Ok();
}
Ouputs:
123
YayThisWorks
Startup.cs add this service
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Your view add inject #inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
get your value
Code
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#{
var id = HttpContextAccessor.HttpContext.Request.RouteValues["id"];
if (id != null)
{
// parameter exist in your URL
}
}
In case you want to access QueryString inside of an asp.net core view you can do it like this:
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#if (Context.Request.Query.Keys.Any())
{
<button>--ClearFilters--</button>
}
we usually can fetch data from routing in 3 way:
1.query string
2.query params
3.hybrid
I describe query string:
exp:
[HttpGet("Home/routing")]
public IActionResult privacy(String name)
{
return ViewModel:name
}
to pass name as querystring:
url:port/Home/routing?name=Alex

Issue using .Net Core 2.2 LinkGenerator

In my controller.Post I am using a link generator to return the endpoint of the newly added resource to the user. The link generator always returns null and I cant quite put my finger on why?
I included controller code that is adding to the database. Note the line calling _linkGenerator.GetPathByAction - the parameters passed in are (in order) 1. the action (in this case the name of the method that does the 'Get'), 2, the controller name and 3 an anonymous type containing the correct Id. I honestly cant see a problem with this.
how the controller name is defined :
namespace CarPriceComparison.Controllers.Api{
[Route("api/vehicles")]
[ApiController]
public class VehicleController : Controller
'Get' Action - function header :-
[HttpGet("{vehicleId_:int}")]
public IActionResult GetVehicle(int vehicleId_)
{
'problem' code that does the get. To me, the parameters passed into _linkGenerator.GetPathByAction look perfectly fine. Suggestions as to why I am always returned a null value?
[HttpPost("")]
public async Task<IActionResult> PostNewVehicleData(VehicleViewModel vehicleData_)
{
try
{
if (ModelState.IsValid)
{
var newVehicle = _mapper.Map<Vehicle>(vehicleData_);
_vehicleRepository.AddVehicle(newVehicle);
var location = _linkGenerator.GetPathByAction("GetVehicle", "vehicles",
new {vehicleId_ = newVehicle.Id});
if (string.IsNullOrWhiteSpace(location))
{
return BadRequest("could not use vehicleId_ to create a new vehicle in the dataase");
}
if (await _vehicleRepository.SaveChangesAsync())
{
var vehicleModel = _mapper.Map<VehicleViewModel>(newVehicle);
return Created(location, _mapper.Map<VehicleViewModel>(newVehicle));
}
}
return BadRequest("Failed to save the vehicle");
}
catch(Exception ex)
{
return BadRequest($"Exception Thrown : {ex}");
}
}
You have to try this:
var location = _linkGenerator.GetPathByAction("GetVehicle", "Vehicle",
new {vehicleId_ = newVehicle.Id});
Or remane your controller to VehiclesController. Then try this:
var location = _linkGenerator.GetPathByAction("GetVehicle", "Vehicles",
new {vehicleId_ = newVehicle.Id});

How to read values from the querystring with ASP.NET Core?

I'm building one RESTful API using ASP.NET Core MVC and I want to use querystring parameters to specify filtering and paging on a resource that returns a collection.
In that case, I need to read the values passed in the querystring to filter and select the results to return.
I've already found out that inside the controller Get action accessing HttpContext.Request.Query returns one IQueryCollection.
The problem is that I don't know how it is used to retrieve the values. In truth, I thought the way to do was by using, for example
string page = HttpContext.Request.Query["page"]
The problem is that HttpContext.Request.Query["page"] doesn't return a string, but a StringValues.
Anyway, how does one use the IQueryCollection to actually read the querystring values?
You can use [FromQuery] to bind a particular model to the querystring:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding
e.g.
[HttpGet()]
public IActionResult Get([FromQuery(Name = "page")] string page)
{...}
You could use the ToString method on IQueryCollection which will return the desired value if a single page parameter is specified:
string page = HttpContext.Request.Query["page"].ToString();
if there are multiple values like ?page=1&page=2 then the result of the ToString call will be 1,2
But as #mike-g suggested in his answer you would better use model binding and not directly accessing the HttpContext.Request.Query object.
ASP.NET Core will automatically bind form values, route values and query strings by name. This means you can simply do this:
[HttpGet()]
public IActionResult Get(int page)
{ ... }
MVC will try to bind request data to the action parameters by name ... below is a list of the data sources in the order that model binding looks through them
Form values: These are form values that go in the HTTP request using the POST method. (including jQuery POST requests).
Route values: The set of route values provided by Routing
Query strings: The query string part of the URI.
Source: Model Binding in ASP.NET Core
FYI, you can also combine the automatic and explicit approaches:
[HttpGet()]
public IActionResult Get(int page
, [FromQuery(Name = "page-size")] int pageSize)
{ ... }
Here is a code sample I've used (with a .NET Core view):
#{
Microsoft.Extensions.Primitives.StringValues queryVal;
if (Context.Request.Query.TryGetValue("yourKey", out queryVal) &&
queryVal.FirstOrDefault() == "yourValue")
{
}
}
You can just create an object like this:
public class SomeQuery
{
public string SomeParameter { get; set; }
public int? SomeParameter2 { get; set; }
}
And then in controller just make something like that:
[HttpGet]
public IActionResult FindSomething([FromQuery] SomeQuery query)
{
// Your implementation goes here..
}
Even better, you can create API model from:
[HttpGet]
public IActionResult GetSomething([FromRoute] int someId, [FromQuery] SomeQuery query)
to:
[HttpGet]
public IActionResult GetSomething(ApiModel model)
public class ApiModel
{
[FromRoute]
public int SomeId { get; set; }
[FromQuery]
public string SomeParameter { get; set; }
[FromQuery]
public int? SomeParameter2 { get; set; }
}
StringValues is an array of strings. You can get your string value by providing an index, e.g. HttpContext.Request.Query["page"][0].
IQueryCollection has a TryGetValue() on it that returns a value with the given key. So, if you had a query parameter called someInt, you could use it like so:
var queryString = httpContext.Request.Query;
StringValues someInt;
queryString.TryGetValue("someInt", out someInt);
var daRealInt = int.Parse(someInt);
Notice that unless you have multiple parameters of the same name, the StringValues type is not an issue.
in .net core if you want to access querystring in our view use it like
#Context.Request.Query["yourKey"]
if we are in location where #Context is not avilable we can inject it like
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#if (HttpContextAccessor.HttpContext.Request.Query.Keys.Contains("yourKey"))
{
<text>do something </text>
}
also for cookies
HttpContextAccessor.HttpContext.Request.Cookies["DeniedActions"]
Maybe it helps.
For get query string parameter in view
View:
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#{ Context.Request.Query["uid"]}
Startup.cs ConfigureServices :
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
I have a better solution for this problem,
request is a member of abstract class ControllerBase
GetSearchParams() is an extension method created in bellow helper
class.
var searchparams = await Request.GetSearchParams();
I have created a static class with few extension methods
public static class HttpRequestExtension
{
public static async Task<SearchParams> GetSearchParams(this HttpRequest request)
{
var parameters = await request.TupledParameters();
try
{
for (var i = 0; i < parameters.Count; i++)
{
if (parameters[i].Item1 == "_count" && parameters[i].Item2 == "0")
{
parameters[i] = new Tuple<string, string>("_summary", "count");
}
}
var searchCommand = SearchParams.FromUriParamList(parameters);
return searchCommand;
}
catch (FormatException formatException)
{
throw new FhirException(formatException.Message, OperationOutcome.IssueType.Invalid, OperationOutcome.IssueSeverity.Fatal, HttpStatusCode.BadRequest);
}
}
public static async Task<List<Tuple<string, string>>> TupledParameters(this HttpRequest request)
{
var list = new List<Tuple<string, string>>();
var query = request.Query;
foreach (var pair in query)
{
list.Add(new Tuple<string, string>(pair.Key, pair.Value));
}
if (!request.HasFormContentType)
{
return list;
}
var getContent = await request.ReadFormAsync();
if (getContent == null)
{
return list;
}
foreach (var key in getContent.Keys)
{
if (!getContent.TryGetValue(key, out StringValues values))
{
continue;
}
foreach (var value in values)
{
list.Add(new Tuple<string, string>(key, value));
}
}
return list;
}
}
in this way you can easily access all your search parameters. I hope this will help many developers :)
Some of the comments mention this as well, but asp net core does all this work for you.
If you have a query string that matches the name it will be available in the controller.
https://myapi/some-endpoint/123?someQueryString=YayThisWorks
[HttpPost]
[Route("some-endpoint/{someValue}")]
public IActionResult SomeEndpointMethod(int someValue, string someQueryString)
{
Debug.WriteLine(someValue);
Debug.WriteLine(someQueryString);
return Ok();
}
Ouputs:
123
YayThisWorks
Startup.cs add this service
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Your view add inject #inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
get your value
Code
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#{
var id = HttpContextAccessor.HttpContext.Request.RouteValues["id"];
if (id != null)
{
// parameter exist in your URL
}
}
In case you want to access QueryString inside of an asp.net core view you can do it like this:
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
#if (Context.Request.Query.Keys.Any())
{
<button>--ClearFilters--</button>
}
we usually can fetch data from routing in 3 way:
1.query string
2.query params
3.hybrid
I describe query string:
exp:
[HttpGet("Home/routing")]
public IActionResult privacy(String name)
{
return ViewModel:name
}
to pass name as querystring:
url:port/Home/routing?name=Alex

How to send a list of integers to web api 2 get request?

I am trying to accomplish this task in which I need to send a list of id's (integers) to a web api 2 get request.
So I've found some samples here and it even has a sample project, but it doesn't work...
Here is my web api method code:
[HttpGet]
[Route("api/NewHotelData/{ids}")]
public HttpResponseMessage Get([FromUri] List<int> ids)
{
// ids.Count is 0
// ids is empty...
}
and here is the URL which I test in fiddler:
http://192.168.9.43/api/NewHotelData/?ids=1,2,3,4
But the list is always empty and none of the id's are passing through to the method.
can't seem to understand if the problem is in the method, in the URL or in both...
So how this is possible to accomplish ?
You'll need custom model binder to get this working. Here's simplified version you can start work with:
public class CsvIntModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var key = bindingContext.ModelName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(key);
if (valueProviderResult == null)
{
return false;
}
var attemptedValue = valueProviderResult.AttemptedValue;
if (attemptedValue != null)
{
var list = attemptedValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).
Select(v => int.Parse(v.Trim())).ToList();
bindingContext.Model = list;
}
else
{
bindingContext.Model = new List<int>();
}
return true;
}
}
And use it this way (remove {ids} from route):
[HttpGet]
[Route("api/NewHotelData")]
public HttpResponseMessage Get([ModelBinder(typeof(CsvIntModelBinder))] List<int> ids)
If you want to keep {ids} in route, you should change client request to:
api/NewHotelData/1,2,3,4
Another option (without custom model binder) is changing get request to:
?ids=1&ids=2&ids=3
Using custom model binder as suggested in comments is the proper way to do this. However, you can also do it the quick-and-dirty way like so:
[HttpGet]
[Route("api/NewHotelData")]
public HttpResponseMessage Get([FromUri] string ids)
{
var separated = ids.Split(new char[] { ',' });
List<int> parsed = separated.Select(s => int.Parse(s)).ToList();
}
First, I'm splitting the uri ids string and then I'm converting them into a list of integers using Linq. Please beware that this is missing sanity checks and will throw expections if the arguments are in incorrect format.
You call it like this: http://192.168.9.43/api/NewHotelData?ids=5,10,20
Update: Personally, I think that using the model binder for a simple thing like this is over-engineering. You need a lot of code to make thing this simple work. The code you would use in a model binder is actually very similar, you would just get a nicer syntax of the method argument. If you wrap the integer parsing into a try-catch block and return an appropriate error message in case of bad format, I don't see a reason why not to use this approach.
[HttpGet]
[Route("api/getsomething")]
public HttpResponseMessage Get([FromUri] params int[] ids)
{
}
Usage: http GET localhost:9000/api/getsomething?ids=1&ids=5&ids=9
Apparently this could work out of the box:
http://192.168.9.43/api/NewHotelData/?ids=1&ids=2&ids=3&ids=4
That is, repeating the parameter name with the different values. But what would happen if those ids became huge and you had to include a lot of them, potentially making the URL too long?
I think it would be cleaner to just make it a POST request and be done with it, though. You wrote that you need the request to be a GET and not a POST, but why? Using POST to retrieve things is perfectly acceptable in the context of AJAX requests.
This might be too tacky, but one could also do something like:
In your .NET class (model):
public class TackyList
{
public IEnumerable<int> myIntList {get; set;}
}
On the client side you'd do a post i.e: {myIntList: [4,2,0]}
Now the action/method on your controller would look something like:
public void myApiMethodThatDoesSomethingWith(TackyList tl)
{
// here you should be able to do something like:
if(tl != null && tl.Count > 0) // blah
}

Categories