mattdwen
mattdwen

Reputation: 5428

WebAPI POST [FromBody] not binding

I'm posting JSON to a WebAPI controller, but the properties on the model are not being bound.

public void Post([FromBody] Models.Users.User model) {
    throw new Exception(model.Id.ToString());
}

The raw request is as follows:

POST http://diva2.local/siteapi/User HTTP/: diva2.local
Connection: keep-alive
Content-Length:: application/json, text/plain, */*
Origin: http://diva2.local
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.: application/json;charset=UTF: http://diva2.local/Users
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=: ISO-8859-1,utf-8;q=0.7,*;q=: .ASPXAUTH=4; __RequestVerificationToken=Rp_hUysjwCjmsxw2

{"Id":3,"FirstName":"DIVA2","LastName":"User1","Username":"diva1","IsApproved":true,"IsOnlineNow":true,"IsChecked":true}

Every example I can find tells me this should work, but model.Id == null.

However, if I change the JSON to:

{User: {"Id":3,"FirstName":"DIVA2","LastName":"User1","Username":"diva1","IsApproved":true,"IsOnlineNow":true,"IsChecked":true}}

everything binds correctly.

This doesn't seem correct. I guess I could accept JObject as the parameter, and bind it up manually, but it feels like the above should Just Work (tm)?

Update:

I've changed the method to return the model, and I still receive null.

public Models.Users.User Post(Models.Users.User user) {
    return user;
}

And the response:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-MiniProfiler-Ids: ["a0fab514-d725-4d8f-9021-4931dc06ec4a","fdeeb9a8-9e36-41d8-91d3-5348e880e193","c1b4cc86-d7c3-4497-8699-baac9fa79bf1"]
X-Powered-By: ASP.NET
Date: Tue, 21 May 2013 09:06:00 GMT
Content-Length: 4

null

Upvotes: 35

Views: 82316

Answers (5)

Chris Chevalier
Chris Chevalier

Reputation: 196

I had the Serializable and xmlElement tags on my class. Even though I specified JSON as the content type in requests and passed a JSON string it wouldn't work till those were removed... It also appears only the serializable attribute was causing the issue.

Upvotes: 0

armstb01
armstb01

Reputation: 653

I found that my class had internal setters on all the properties.

Changing public <property> {get; internal set;} to public <property> {get; set;}

fixed my issue

Upvotes: 8

mattdwen
mattdwen

Reputation: 5428

My user model did not have a parameter-less constructor.

Upvotes: 16

Kiran
Kiran

Reputation: 57949

You are missing the Content-Type header in your Request.

Unfortunately even if you had checked for ModelState, we are not throwing any error information. However, the good news is that this behavior has been fixed for our coming release and you would see a 415 status code based response.

Web API requires the Content-Type header to find out the right formatter to deserialize the body to the parameter on the action.

Upvotes: 25

Darin Dimitrov
Darin Dimitrov

Reputation: 1038770

The following works perfectly fine for me:

Model:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }
    public bool IsApproved { get; set; }
    public bool IsOnlineNow { get; set; }
    public bool IsChecked { get; set; }
}

Controller:

public class ValuesController : ApiController
{
    public User Post(User user)
    {
        return user;
    }
}

Request:

POST http://example.com/api/values HTTP/1.1
Connection: keep-alive
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Host: localhost:8816
Content-Length: 125

{"Id":3,"FirstName":"DIVA2","LastName":"User1","Username":"diva2user1","IsApproved":true,"IsOnlineNow":true,"IsChecked":true}

Response:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Date: Tue, 21 May 2013 08:59:02 GMT
Content-Length: 125

{"Id":3,"FirstName":"DIVA2","LastName":"User1","Username":"diva2user1","IsApproved":true,"IsOnlineNow":true,"IsChecked":true}

As you can see everything is bound fine and dandy without the User prefix in the request JSON payload.

Be careful with the model.Id, because id might have a special meaning if you are using it in your route definitions as being part of the route. Do not confuse the 2 things (route parameters and those coming from the request body payload).

Upvotes: 19

Related Questions