I'm trying to post a JSON object to a Web Api URL and it's not binding to the model.
This seems to be the same problem: ASP.Net Web Api not binding model on POST
I tried everything that they did and it still doesn't work. The one difference you may notice is that I'm not using the DataContract attributes, but I don't believe they should be required, and didn't make any difference when I tried them.
public class MyModel
{
public int Id { get; set; }
}
Public class MyController : ApiController
{
public int Save(MyModel myModel)
{
// myModel is always null
return 0;
}
}
You appear to be missing [HttpPost] attribute from your controller method. It appears in the above case this is actually not strictly required, perhaps this is only needed when posting primitives?
Also just as a note I would use a more REST based syntax if you are using WebApi for example use methods Get, Post, Put ect on your controller rather than named methods
EDIT:
You also have one other really subtle issue with your post. A header line cant end with a ; so Content-Type: application/json; charset=utf-8; should be Content-Type: application/json; charset=utf-8
It could be an encoding issue.
I changed my encoding and model binding is done succesfully.
client.Encoding = Encoding.UTF8;
Related
I'm trying to setup an MVC app that has an endpoint that accepts XML in the request body, and then processes it. I've dumbed this down to try and narrow down the problem, so here is my model:
Public Class MyModel
Public Property Value1 As String
Public Property Value2 As String
End Class
And I have this setup in my controller:
<HttpPost()> _
Public Function Launch(<FromBody()> ByVal myModel As MyModel) As ActionResult
' ...
' myModel is always a new object but with null properties
' ...
End Function
And here is the HTTP request I'm sending:
POST /launch HTTP/1.1
Host: example.com
Accept: application/xml
Content-Type: application/xml
<MyModel>
<Value1>1</Value1>
<Value2>2</Value2>
</MyModel>
When I debug, it hits the correct endpoint but myModel is always empty. And by empty I mean the object has been instantiated, but all the properties of it are null.
I've searched all over stackoverflow for an answer, and nothing has worked. I have added config.Formatters.XmlFormatter.UseXmlSerializer = True in the Global.asax code but that didn't seem to work. I've also added a custom XmlMediaTypeFormatter that ignores xml namespaces which doesn't seem to do anything either.
Is there something blatantly obvious that I'm missing? I know I'm using VB.NET and all the examples I've seen are C#, so maybe something was lost in translation. At this point I'm having a hard time understanding why this is so hard to do.
The issue is because my controller was of type System.Web.Mvc.Controller and not System.Web.Http.ApiController. MVC controllers do not have the same deserialization abilities that WebAPI controllers have, and thus expect either JSON data or HTML form data as the payload. After changing the class of the controller to System.Web.Http.ApiController it worked.
I have several Odata(4.0)/Rest controllers in my project(c#/.net 4.62). All but 1 work as expected. This is the controller that returns a 406 every time I call it.
[RoutePrefix("api/SecurityConfig")]
[ApiExplorerSettings(IgnoreApi = false)]
public class SecurityConfigController : ODataController
{
[Route("GetRoleGroupWithRoles")]
[EnableQuery(MaxExpansionDepth = 6)]
[HttpGet]
public virtual async Task<IHttpActionResult> GetRoleGroupWithRoles([FromODataUri] Guid key)
{
return Ok("Hello World");
}
}
I call using the following headers:
Accept: application/* (though I've tried just "*" and "application/json")
Content-Type: application/json
I'm baffled why this controller is not working and the rest of them are......thanks in advance for any thoughts on how I can figure this out.
I think you are missing the first two steps of building an OData service. ODataController, as the name says, only works with OData routes. You need to build an EDM model representing your OData service, and, add an OData route exposing that EDM model. Refer to this official documentation and blog post for details on how to build OData services. [source]:How can I avoid a 406 when receiving an OData.PageResult<T>?
I have this controller action :
[HttpPost]
[ActionName("aaa")]
public HttpResponseMessage aaa(Z z) //notice - no [FromBody]
{
return Request.CreateResponse(HttpStatusCode.OK, 1);
}
Where Z is :
public class Z
{
public string a { get; set; }
}
But when I post via fiddler :
POST http://localhost:11485/api/profiles/aaa HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: localhost:11485
Content-Length: 3
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,he;q=0.6
Cache-Control: no-cache
Connection: keep-alive
a=1
I can actually see it is working :
Question:
If so , how does it working without the [FromBody] attribute ? and do I still need /not write this attribute ?
Also , what is the scenario where not writing this attribute - will cause problems ?
Binding is different for MVC and Web API. By default, ASP.NET Web API binds complex types from the request message body and simple types from URI, query string, etc. Since you specified Z, which is a class (complex type), it populates the action method parameter from body without you having to specify [FromBody]. On the other hand, if your request is http://localhost:11485/api/profiles/aaa?z=1 without a body, it will NOT bind to your complex parameter automatically. In that case, you will need to specify [FromUri] like this: public HttpResponseMessage aaa([FromUri]Z z).
On the other hand, say your action method is public HttpResponseMessage aaa(string a). We now have string, which is a simple type. In this case, http://localhost:11485/api/profiles/aaa?a=1 without a message body will let Web API bind the parameter correctly without you having to specify [FromUri]. Now, if you want to bind from body in this case, you will need to specify public HttpResponseMessage aaa([FromBody]string a). Of course, for this body must be =1, for application/x-www-form-urlencoded, without the key name of a.
Bottom line - your parameter (simple type or complex type) determines how Web API binds. To make it work differently from the default behavior, you need to tell via FromUri or FromBody.
PS. Whatever I mentioned above holds good purely for the good old ASP.NET Web API (including 2). In ASP.NET 5.0 a.k.a ASP.NET vNext or ASP.NET MVC 6.0, MVC and Web API have been unified. In order for it to bind a complex type from body, you must specify [FromBody].
I have the following DTO where the URI should be like api/logs?verbose=, where verbose can be true or false.
[Route("/api/logs", "GET")]
public class GetLogs
{
public bool Verbose { get; set; }
}
My service is:
public object Get(GetLogs getLogs)
{
//For brevity
return getLogs;
}
I see that the sample that generated in metadata document mentions POST instead of GET, which I explicitly set.
POST /json/oneway/GetLogs HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: length
{"Verbose":false}
How can I fix this?
To my knowledge, that is just a sample. Don't take the "POST /json/oneway/GetLogs HTTP/1.1" literally. You should be using the actual Route endpoints shown earlier in the same page, which would likely be:
The following routes are available for this service:
GET /api/logs
In my ServiceStack projects, they are all the same way - the sample is POST, even if the available routes are only: GET, OPTIONS
I did some searching, and this appears to be hardcoded in the class: ServiceStack.Metadata.OperationControl
So you could accept you "can't do it" with the current release. Or figure out how to override or patch OperationControl template rendering.
Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.