Web Api string parameter (fromQuery) always null when posted with value (FromBody) - asp.net-web-api2

I am using web api 2 and .net core 2.0
I'm trying to post to an endpoint but the propertyId in the in the query is always null, but the value in the body is populated. If I change propertyId to an int, it gets populated.
After LOTS of reading I found this, which has "confirmed" - for me - that what I've done should be working. It's not so I don't know what I'm missing. There are many threads on this issue but none have helped me. Could someone please advise what i'm missing?
I have tried a number of variations, including:
passing the query as ?propertyId=teststring
Removing [FromQuery]
Removing Route
Changing headers
Combos of above
public class RoomsController : ControllerBase
{
...other stuff...
[HttpPost, Route("{propertyId}")]
public async Task<IActionResult> Post([FromQuery]string propertyId, [FromBody]List<CreateRoomRequestDto> value)
{
List<Guid> result = await _mediator.Send(new NewRoomRequest() { PropertyId = propertyId, NewRoom = value });
return Ok(result);
}
}
http://localhost:49942/api/Rooms/testString
the postman script
POST /api/Rooms/testString HTTP/1.1
Host: localhost:49942
Cache-Control: no-cache
Content-Type: application/json
Postman-Token: f15b569d-84a2-4bde-bdf0-b1cdf3fff975
[
{
"Tag":"30dd879c-ee2f-11db-8314-0800200c9a66",
"Name":"testName",
"Description": "tesDescription",
"Length": 1.5,
"Width": 1.8,
"Dimension": 5,
"DimensionText": "some DimensionText test",
"PhotoUrls": ["klklkl", "oioioioii"]
},
{
"Tag":"30dd879c-ee2f-11db-8314-0800200c9a66",
"Name":"testName2",
"Description": "tesDescription2",
"Length": 1.2,
"Width": 1.9,
"Dimension": 5,
"DimensionText": "some DimensionText test2",
"PhotoUrls": ["klklklwewe", "oioioioiinmnmnm"]
}
]

What you need to do is name the [FromQuery] attribute like this
[FromQuery(Name = "propertyId")]
I've been looking through the .NET Core routing code recently and it seems like [FromQuery] just needs some help, from time to time, to know which pieces of the query string to map from.
EDIT: Oh, and also take your route attribute off so that it's clear where the parameter is coming from. As well as make your request use an actual query string like
?propertyId=testString instead of making it the ID on the route.

Related

AspnetCore Odata 7 Returning class name in context and odata.type unexpectedly

I am using asp.net core 6 and Microsoft.AspNetCore.OData version 7.5.12, I realize this is an older version of the package but I'm pinned to that currently for reasons out of my control.
My situation is I have a single controller that can return a couple of different kinds of entities. Lets say I have dogs and each dog could have walks, so I have the routes:
/api/v1/dogs
/api/v1/dogs/{dog_id}/walks
the first returns a list of dogs and the second returns a list of walks for a given dog with the id of dog_id
To build my edm model I am doing:
private IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Dog>("dogs");
builder.EntitySet<Walk>("walks");
return builder.GetEdmModel();
}
to map my routes I am doing:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var edmModel = GetEdmModel()
app.UseEndpoints((Action<IEndpointRouteBuilder>)(endpoints =>
{
endpoints.Select().Filter().Count().OrderBy().Expand().MaxTop(new int?());
endpoints.MapODataRoute("dogs", "api/v1", edmModel);
endpoints.MapODataRoute("walks", "api/v1/dogs/{dogId}/", edmModel);
....
On my controllers I have the [EnableQuery] attribute on each of the action methods
[Route("api/v1/dogs")]
public class DogController
...
[HttpGet]
[EnableQuery]
public async Task<IActionResult> GetDogs()
{
var dogs = await _dogRepo.GetAllAsync(); // Returns Enumerable<Dog>
return Ok(dogs)
}
[HttpGet("{dogId}/walks")]
[EnableQuery]
public async Task<IActionResult> GetWalksAsync([FromRoute] Guid dogId)
{
var walks = await _walkRepo.GetForDog(dogId); // Returns Enumerable<Walk>
return Ok(walks)
}
Both of these endpoints seem to be working, I can pass odata query params like $count, $top and see the actions take effect, however the payloads from these two endpoints differ and I'm not sure why. The request to /api/v1/dogs returns:
{
"#odata.context": "http://localhost/api/v1/$metadata#dogs",
"value": [
{
"Id": "bc576d83-dc33-4c31-988d-53543f4f01f0",
"Name": "Dog1"
},
{
"Id": "baec93fa-344c-11ed-a261-0242ac120002",
"Name": "Dog2"
}
}
which is fine, however thee request to /api/v1/dogs/{dog_id}/walks returns the fully qualified class name for walk in the #odata.context attribute and also includes a #odata.type property that has the fully qualified class name:
"#odata.context": "http://localhost/api/v1/$metadata#Collection(My.Project.My.Model.Namespace.Walk)",
"value": [
{
"#odata.type": "#My.Project.My.Model.Namespace.Walk",
"Id": "03ebcaf6-f94b-48e6-854b-c156a993fcc9",
"DogId": "bc576d83-dc33-4c31-988d-53543f4f01f0",
"WalkTime": "2022-09-14T08:59:57.765216-03:00"
},
I'm really unsure what I am missing here. I feel like I am configuring these similarly, the fact that I can perform odata operations on both of these endpoints suggests to me these are both configured appropriately. I can't find a reason for this descrepancey and not seeing anything in the documentation, especially for this version of Microsoft.AspNetCore.OData, I feel like I am missing something simple here. I did see the post Why does my OData return data contain "#odata.type"? in which the advice is given to modify the client's request however I don't think that would apply to me because I am using the same client, same headers etc, for each endpoint request and I am getting the differing responses. I would also prefer my API to be consistent.
If anyone could suggest what I'm doing wrong here I'd really appreciate the help. I've been banging my head against this for days. Thanks very much!

How to send a List<Map<String, String>> as parameter for a GET API

I have a requirement to pass List<Map<String, String>> as a parameter for REST GET API.
I need help to know how this can be passed from Postman or similar tool.
I tried to set it as a BODY for a GET API, it is giving me errors.
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.
Any help is appreciated.
You can very well !
I tried and this worked for me
Create a model class which has variable of type List<Map<String, String>> myList;
Define a controller similar to below
#PostMapping("/addList")
public ResponseEntity<List<Map<String, String>>> passList(#RequestBody ListModel listModel) {
System.out.println("List mapped " + listModel);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Create a request from Postman or any tool like this
{
"myList": [
{
"one": "1",
"two": "2"
}
]
}
Response I got
List mapped ListModel [myList=[{one=1, two=2}]]
Make sure you map correct variable name ( for e.g. I have defined myList, so that must be passed so it gets properly mapped in Controller class ) also assuming toString, GetterSetters , and your familiarity with few basic REST annotations related to Spring/SpringBoot :)

JSON object format

This may be a stupid question but I want to ask because it may be an indication of a mistake I may be making.
I just created my first Web API project and started hosting it as a website. Initially, I was getting XML responses, so I added the following line in the Register method:
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
I now get JSON results but my JSON results are not nicely formatted. They all come back as a regular string.
I get this:
{ "id": 123, "firstName": "John", "lastName": "Smith",
"gender": "male"}
I've seen a lot of examples where the result looks like this:
{
"id": 123,
"firstName": "John",
"lastName": "Smith",
"gender": "male"
}
Am I doing something wrong?
-- Edit ---
Thank you all for your response.
I wonder if the way I'm returning the data is causing this formatting issue. I'm just returning my POCO class and lettting the Web API handle any serialization. Is this the right way to return my data?
public IHttpActionResult GetSpecifiedPerson(int id)
{
Person user = new Person();
user.PersonId = 1234567;
user.FirstName = "Jane";
user.LastName = "Doe";
return Ok(user);
}
There is nothing wrong with your JSON. When you parse that you won't face any problems.
your JSON result is completely fine. nothing wrong.
most examples are formatted nicely for readability. nothing else

ASP.Net MVC Api won't accept an int parameter

I have the following code:
[HttpPost]
public async Task<ReturnStatus> Delete([FromBody]int id)
{
await new BusinessLogic.Templates().DeleteTemplate(id);
return ReturnStatus.ReturnStatusSuccess();
}
When I run this as an AJAX request, the id is null. I've inspected the data coming in through Fiddler and the body is:
{"id":"11"}
The header has Content-Type: application/json; charset=UTF-8.
If I modify the code slightly to
[HttpPost]
public async Task<ReturnStatus> Delete([FromBody]string id)
{
await new BusinessLogic.Templates().DeleteTemplate(Convert.ToInt64(id));
return ReturnStatus.ReturnStatusSuccess();
}
it works just fine.
What am I doing wrong here?
Please read this part, number 3 in particular:
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
3. [FromBody] parameters must be encoded as =value
(quoting the section for future reference:)
There are two ways to make jQuery satisfy Web API’s encoding requirement. First, you can hard code the = in front of your value, like this:
$.post('api/values', "=" + value);
Personally, I’m not a fan of that approach. Aside from just plain looking kludgy, playing fast and loose with JavaScript’s type coercsion is a good way to find yourself debugging a “wat” situation.
Instead, you can take advantage of how jQuery encodes object parameters to $.ajax, by using this syntax:
$.post('api/values', { '': value });

How to format iqueryable mvc 4 web api requests?

I'd like to use iqueryable on all my collections so that I get all of the odata features. However I need to format the response of the request with the following fields;
{
href: "url to resouce",
length: 10,
items: [
"(IQueryable results)"
]
}
Formatting the response isnt the hard part but keeping all of the odata "stuff" working is.
So my code looks like;
MyFormattedResponse.Href = "poop.com";
MyFormattedResponse.Length = 0;
MyFormattedResponse.Items = _myRepo.Gets();
Request.CreateResponse(HttpStatusCode.OK, MyFormattedResponse);
But the error I get is:
The action 'Get' on controller 'MyResource' with return type
'MyObject' cannot support querying. Ensure the type of the returned
content is IEnumerable, IQueryable, or a generic form of either
interface.
Is this something I can construct in a media formatter or perhaps a filter?
I really want to keep the odata awesomeness...
Take a look at this answer I've provided:
Web API Queryable - how to apply AutoMapper?
Your case is similar, you can do something like this:
public MyFormattedResponse Get(ODataQueryOptions<Person> query)
{
var results = query.ApplyTo(_myRepo.Gets()) as IEnumerable<Person>;
return new MyFormattedResponse() { Href = "foo.com", Length = 5, Items = results };
}
Make sure you remove the [Queryable] attribute.