What would be the best way of receiving IEnumerable as a URI-parameter for a GET endpoint in .NET Core 3.x
In deeper context: What I want to do is filtering out data depending on a number of GUIDsĀ“.
So far I've got
[HttpGet]
public ActionResult Get(int allowedMissing, IEnumerable<Guid> ids)
.
.
.
Im calling the endpoint using Postman as such:
/endpoint?allowedMissing=2&ids=cab0cfcb-8eb2-4313-8064-e7b0841d4f8a&ids=b2b944c2-6b4a-4c32-9a2f-472b891e4843
This results in a 415 Unsupported Media Type without hitting any breakpoints inside the controller.
Ok, so default for receiving IEnumerable in an Action seems to be in body and will require decorating the parameter with FromQuery if sent as query.
Changing to this solved my problem:
[HttpGet]
public ActionResult Get(int allowedMissing, [FromQuery] IEnumerable<Guid> ids)
.
.
.
Related
I haven't messed with OData in a while, but I remember it being really useful. So I've gone for a .NetCore 3.1 EFCore + OData architecture for my API. With a view to make it fully generic etc. etc.
Doing a little test, I can get the expected results from my browser: e.g.
https://localhost:44310/things?someidfield=44
Cool I get back the JSON I was expecting! But it's sooo slow, why? Looking at the SQL (Profiler) I can see it has no WHERE clause, it's getting everything from the database and filtering it in memory, on half a million records?
What am I missing here? I've tried a number of ways to write the GET method. Starting with not passing any queryOptions (which worked! but same result underneath) and then the below where I explicitly apply the options to by EFCore entity.
[HttpGet]
[EnableQuery]
public async Task<IEnumerable<thing>> GetThingsAsync(ODataQueryOptions<thing> queryOptions)
{
return await queryOptions.ApplyTo(DB.thing).Cast<thing>().ToListAsync();
}
The result set is being loaded in memory because you're calling ToListAsync() and returning an IEnumerable.
If your GetThingsAsync method returns an IQueryable<T> (instead of IEnumerable<T>), then the query will be applied to the database and only the filtered data will be fetched.
If DB.thing is an EFCore DbSet (which implements IQueryable<T>), then you can simplify your method as
[HttpGet]
[EnableQuery]
public Task GetThingsAsync()
{
return DB.thing;
}
Furthermore, like some in the comment already mentioned, the correct syntax for filtering in your case would be ?$filter=someidfield eq 44
I'm currently doing this:
[HttpPut]
public void Edit(int id, Model model)
{
...
}
Which gives me the endpoint /api/controller?id=66 instead of what I want: /api/controller/66
To get what you want -- api/controller/66 on your PUT request, your HTTP verb attribute should be modified to [HttpPut("{id}")]
And the further reason why your id is obtained from the query string by default is that the parameter binding in the case of PUT request works in such a way that the primitive type is bound from request query string and the complex type from request body.
A brief of the parameter binding rules is listed out in this answer.
I have a REST interface endpoint like
POST /items/12345/actions
I utilize a generic actions sub collection to be apply to apply changes to 12345 which are not easily mapped to the content or direct other sub collections of it.
My question is the following: Since there could be multiple different action types I identify the action by a JSON property of the content of an uploaded document.
How do I select a action by a part of the JSON body of the request. Is there something possible like...
[Route("api/v1/items")
public class ItemsController : Controller
{
[HttpPost("{id}/actions")]
[CheckJsonBody("type", "ActionA")]
public ActionResult DoActionA(int id, ActionA a)
{
// do something
}
[HttpPost("{id}/actions")]
[CheckJsonBody("type", "ActionB")]
public ActionResult DoActionB(int id, ActionB b)
{
// do something
}
}
The request would look like ...
{
"type": "ActionA",
"abc": "xyz"
}
I have digged myself up into the code till Microsoft.AspNetCore.Mvc.ActionConstraints.ActionMethodSelectorAttribute (GitHub).
However starting from there, I am a bit lost to reach a high-performance solution. Do I need to decode the body or is that something which is already done at that time the constraint is evaluated?
ps: And yes, I know I could handle them in one action and do a switch on the "type" property.
An ASP.NET team member was so friendly to direct me to an answer: In the ActionMethodSelectorAttribute you can read the body into a memory stream, read till the property for the selection filter. Then you seek the memory stream to zero and replace it in the request (for later model binding). You can cache the criteria value in HttpContext.Items to speed it up if you use the same property for multiple actions.
I had an MVC controller with url which had a parameter binding with square braces in it like
public Product GetProduct([Bind(Prefix = "product[productid]") int id)
which used to work with the request url like
http://localhost:8080/Product/GetProduct?product[productid] = 1001.
I'm able to get the value for id as 1001 in MVC controller. Unfortunately I'm supposed to change this to an web api controller and I have the api controller method defined like this
public Task<Product> GetProduct([FromUri(Name=product[productid])] int id)
for which i get a 404 response with the same request url . The [FromUrl] works with the parameter name without braces when
defined like
public Task<Product> GetProduct([FromUri(Name=productid)] int id)
and the url like
http://localhost:8080/Product/GetProduct?productid = 1001
Is there any workaround for this apart from parsing the request url with braces and get the value.
Your URL is right. You should fix the controller the following way:
public Task<Product> GetProduct([FromUri(Name=product.productid)] int id)
I don't really know why this works.
This is where I'm getting my error.
#foreach(var entry in ViewBag.Entries){...}
I stepped through the program after entering values into a form.
The Entries are hitting the dB and in my controller it showed that the linq code hit the DB but when I returned the view, the object wasn't found?
here is the code in my controller
public ActionResult Index()
{
var mostRecentEntries =
(from entry in _db.Entries
orderby entry.DateAdded descending
select entry).Take(20);
return View();
}
If it'll help you this is coming straight out of a book ASP.net mvc 4 in Action by the Manning company around page 34.
As the error is trying to tell you, ViewBag.Entries is null.
If you want to use something in ViewBag, you need to put it there in the controller.