Webapi model binder not constructing a complex type? - asp.net-web-api

I have this api action :
[Route("~/api/products/{productCode}/price")]
public IHttpActionResult PutProductPrice(string productCode,
PriceBindingModel binding) {
when I send a request to this endpoint with no request body like this :
Accept: application/json
Content-Type: application/json
Content-Length: 0
In the body of the PutProductPrice binding is null. (different from MVC)
I'm used to Model Binder create a PriceBindingModel using it's default parameterless constructor and assigning the values (in this case there is none)
Is this supposed behavoir ? should I check if Model is null in all my api requests ?
I guess this is a bug or some other problem I'm not seeing.
Note: Sending {} (empty object) on the request, makes the BindingModel be created and getting the expected bad request with a failed modelstate. (which I supposed should happen in the first scenario (sending an empty body) as well.
Note 2: doing this in the first line on the controller make it work as expected:
binding = binding ?? new PriceBindingModel();
ideas ?

Related

Spring receiving empty body

I'm using Spring for my backend. I have the following code:
#RequestMapping(value = "/test", method = RequestMethod.POST)
#CrossOrigin
public void test(HttpServletRequest request) {
System.out.println(request.getParameterMap().size());
System.out.println(new JSONObject(request.getParameterMap()));
}
When I send JSON data using Postman, I get a map of all the parameters I've sent.
But when I'm making the same call from my website, I get an empty map with size 0. I do not get any error or exception on both front and back sides.
What could be the reason?
Thank you
Most probably getParameterMap() is really empty in your case, because parameters are not passed as query, but as a body (content) of HTTP request when it is sent from your web-site.
It can also be affected by the Content-Type and Accept headers of the HTTP request.
According to official documentation ServletRequest.getParameterMap() returns:
For HTTP servlets, parameters are contained in the query string or posted form data.
Usually "posted form data" implies: HTTP header Content-Type: application/x-www-form-urlencoded and URL encoded name-value pairs of parameters in the content of HTTP request.
If your web-site sends application/json, any other content type, or does not define content type at all, it might not be properly mapped into the request parameters by servlet container. In this case you should look into the body of HTTP request (ServletRequest.getReader()) to get the payload, or let Spring MVC do that (e.g. #RequestBody annotation).

Multiple Content-type in Spring MVC

Can we have multiple content-type in Spring MVC request header?
I'm passing:
{Content-type = application/json, text/plain}
through Postman to my API. Currently, I'm getting org.springframework.web.HttpMediaTypeNotSupportedException: Invalid mime type ....
I wanted to know, is there something with my input values, or we can't have multiple content-type in our header.
Controller:
#RequestMapping(value = "/addressees", produces = APPLICATION_JSON_UTF8_VALUE, method = GET)
Yes, spring mvc request mapping supports multiple consumes MIME type , sample looks like
#RequestMapping(value = "/something", method = PUT,
consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE},
produces = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public SomeObject updateSomeObject(SomeObject acct) {
return doStuff(acct);
}
Add consumes part in requestmapping like - consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE}
For know more, refer this link -
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html
Your request header can have one content-type per request. You specify to the server what type of data are actually being sent.
Your server/API endpoint can support multiple.
So if your request specifies both application/json and text/plain at the same time, I believe it is a problem with your request.
Yes, RequestMapping.consumes accepts an array of Mime types
String[] consumes() default {};
Note that you have to use consumes to define the incoming MIME types. produces is for the outgoing type.

Web API 2 attribute routing returning 404

I'm having trouble getting the Web API 2 attribute routing to work.
I've been trying everything I could find this whole evening but I can't find the problem.
What I want to achieve is the following:
Make a POST request to http://localhost:xxxx/api/chat/joingroup/1234 to get to the following API call:
[Route("joingroup/{id}")]
[HttpPost]
public async Task<IHttpActionResult> JoinGroup(string id, string connectionID)
{
await hubContext.Groups.Add(connectionID, id);
return Ok(hubContext.Groups.ToString());
}
This keeps getting me a http 400 message.
{"message":"No HTTP resource was found that matches the request URI 'http://localhost:41021/api/chat/joingroup/123'.",
"messageDetail":"No action was found on the controller 'Chat' that matches the request."}
But sending a post to: http://localhost:41021/api/chat/sendmessage/pm/123123 and also to http://localhost:41021/api/chat/joingroup gives me a 200
The chatcontroller:
[RoutePrefix("api/chat")]
public class ChatController : ApiController
{
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
[...]
[Route("joingroup/{id}")]
[HttpPost]
public async Task<IHttpActionResult> JoinGroup(string id, string connectionID)
{
await hubContext.Groups.Add(connectionID, id);
return Ok(hubContext.Groups.ToString());
}
HTTP POSTS to http://localhost:xxxx/api/chat/sendmessage are working fine.
I cannot figure out why it isn't going to the correct method when I'm calling a POST on http://localhost:xxxx/api/chat/joingroup/1234.
SOLUTION:
The solution was to reference both values that are needed in the JoinGroup method, id and connectionID. Now the request will hit this method.
Using:
http://localhost:xxxx/api/chat/joingroup/john?connectionid=123 will work.
I noticed two things on the code you sent through:
the path you POST to is: localhost:xxxx/joingroup/1234 , this
should be localhost:xxxx/api/chat/joingroup/1234
because you have 2 parameters for the joingroup, you will need to pass both of them through, may be like this localhost:xxxx/api/chat/joingroup/1234?connectionID=value or you can pass it on the request body
if the connectionID is optional you can modify the method to use option al parameters like this
public string JoinGroup(string id, string connectionID = "")
please let me know if this helps.
Thanks
Ashraf
I assume the connectionID parameter references the POSTed data. The easiest thing to make it work is to decorate it with the [FromBody] attribute and put an = in front of the value being sent like this: =MyConnection1.
Web API expects an object with properties or an array otherwise. Alternatively, you can wrap the connection ID with a custom class and pass it serialized as JSON/XML.

QueryString truncated in WebAPI RequestBody when request is manually composed?

I've confirmed I have the dropdown set to post and the URL is correct, because the string gets passed into my Web API project without error. However it is cutting everything off of the string after the first parameter
Request Headers:
User-Agent: Fiddler
Host: localhost:52888
Content-Length: 35
Content-Type: application/x-www-form-urlencoded
Request Body:
=name=TestName&date=10/15/2014
In the Web API project the only part that is passed is name=Test Name
I'm confident the query string is in the right format. I'm wondering if anyone else can point me in the direction of what I might be missing.
If I remove the = in front of the request body then nothing is received
A signature like AddCourse([FromBody] string courseRequest) tells WebAPI to look for a POST parameter named courseRequest. But (when it is properly formatted) your request body doesn't have that parameter - instead it has name and date. When you misformat the request body by prepending an = character, it apparently causes the parser to decide that name=test is the value. But the second part of the query string is after an &, and is clearly a different parameter. It has nowhere to bind that parameter, so it just gets dropped.
There are at least two solutions here. One would be to pass the parameters on the query string instead of in the request body, and use a method signature like: AddCourse(string name, string date) (note removed of [FromBody]).
Another would be to create a model object that encapsulates the request, something like
public class AddCourseModel{
public string Name {get;set;}
public string Date {get;set;}
}
and use that as the argument to your method: AddCourse([FromBody] AddCourseModel model).

Web API: Make action parameter mandatory

I have an action that looks thus:
[HttpPost]
public HttpResponseMessage PostInstantiation(Guid id, [FromBody]Instantiation instantiation)
If the user does a POST to the correct URL, but with no (/empty) body, then model validation doesn't fail, and the instantiation argument is null. I expected it to fail.
What is the correct way of handling this scenario?
What you are noticing is an expected behavior. If you indeed would like to check if the user has sent an empty body, then you could probably check for the Content-Length header of the incoming request.
if(Request.Content.Headers.ContentLength == 0)

Resources