0

I'm using Ajax to make model and send it to the controller.

Here is Model:

 public class PersonDto{
        public Guid Id { get; set; }
        public int PersonAge { get; set; }
        public string PersonName { get; set; }
        public DateTime? DateCreated { get; set; }
    }

Here is Controller:

[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson(PersonDto offer) {
  //Save to Entity FW
}

Here is Ajax:

var data = {
    Id: $('#personModal #personModalTitle').text(),
    PersonAge: $('#personModal #personId').val(),
    PersonName: $('#personModal #personName').val()
};

var dataJson = JSON.stringify(data);
console.log(dataJson);

$.ajax({
    type: 'POST',
    url: 'Persons/EditPerson',
    data: dataJson,
    contentType: "application/json",
    success: function (result) {

    },
    error: function (error) {
        Alert("Error Saving offer changes!");
    }
});

Here is Console Log (Json), everything looks great:

{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}

When I Debug PersonsController I see that my model is never populated, is not null, just no data in it.

I tried Adding DateCreated to Model in Ajax function, I tried parsing Age to int.

I tried adding [FromBody] annotation to PersonDto in Action, then I getting NULL to my model.

I don't know where I can make error here.

ElementW
  • 29
  • 8
  • `InstallerId` does not exist in the `PersonDto` poco. – IronAces Aug 24 '18 at 09:24
  • By default aspnet core expects camelCased properties (unless you condifugured your api to accept otherwise). Therefore it could be expecting `{ "id": ..., "personAge":..., "personName":...}` instead of `{ "Id": ..., "PersonAge":..., "PersonName":...}`. – Gerald Chifanzwa Aug 24 '18 at 09:29
  • @DanielShillcock I changed, I copied ajax from different answer and change it. Here is ajax from my code. – ElementW Aug 24 '18 at 09:29
  • @gerryc.inc I changed, and same result: Here is Json now: {"id":"2e454721-99c5-47b7-abfa-f2e410cccaaa","personAge":"57","personName":"John snow"} – ElementW Aug 24 '18 at 09:32
  • I reproduced your code, it worked when i did `camelCase` properties, with the `[FromBody]` attribute on the action parameter, and together with `'Accept': 'applicaiton/json'` header set in my request. – Gerald Chifanzwa Aug 24 '18 at 09:45
  • Possible duplicate of [Asp.net Core 2 API POST Objects are NULL?](https://stackoverflow.com/questions/45862459/asp-net-core-2-api-post-objects-are-null) – Kirk Larkin Aug 24 '18 at 10:10

3 Answers3

1

For JSON encoded body , just add a [FromBody] :

    [Route("EditPerson")]
    [HttpPost]
    public async Task<IActionResult> EditPerson([FromBody] PersonDto offer)
    {
        return new JsonResult(offer);
    }

Update :

  1. Another way to do that is use a [ApiController] to decorate the controller class .

  2. If there's no [ApiController] , you should add a [FromBody] to enforce the Model Binder to read from body .

  3. Whether a camelCased or pascalCased payload or not important . It makes no difference .

Camel Case :

POST https://localhost:44372/api/values/EditPerson HTTP/1.1 
Content-Type : application/json

{"id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","personAge":"5","personName":"John"}

Pascal Case :

POST https://localhost:44372/api/values/EditPerson HTTP/1.1 
Content-Type : application/json

{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
itminus
  • 23,772
  • 2
  • 53
  • 88
  • That is answer COMBINING it with @gerryc.inc answer from the comments: By default aspnet core expects camelCased properties! Without camelCased properties, it won't work with [FromBody] annotation. I tried already. – ElementW Aug 24 '18 at 09:34
  • How does this work exactly? `FromBody` is intended to be used on simple types according to [this](https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#using-frombody) – Hintham Aug 24 '18 at 09:35
  • @AleksandarGas , actually , if the client posts a payload `{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}` , it will also work. – itminus Aug 24 '18 at 09:37
  • @Hintham , The [FromBody] will force the Model Binder to read from body . *In MVC simple types are any .NET primitive type or type with a string type converter* , see https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1 – itminus Aug 24 '18 at 09:45
0

Can you try to get your data as a JObject:

[FromBody] JObject offer

[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] JObject offer) {
    string Id = (string)form.SelectToken("Id")
    string PersonAge= (string)form.SelectToken("PersonAge")
    string PersonName= (string)form.SelectToken("PersonName")
}
Yanga
  • 2,885
  • 1
  • 29
  • 32
0

As I can see from your code you are not sending variable, the one you expect, "offer". When doing AJAX call and sending data, data parameters should be named as those you are expecting.

You should send data in this format:

{ 
  offer: { PersonAge: 10, PersonName: 'Senad' } 
}

Your call should look like this

var data = {
    Id: $('#personModal #personModalTitle').text(),
    PersonAge: $('#personModal #personId').val(),
    PersonName: $('#personModal #personName').val()
};

$.ajax({
    type: 'POST',
    url: 'Persons/EditPerson',
    data: { offer: data }, //no need to serialize it
    dataType: 'json',
    success: function (result) {

    },
    error: function (error) {
        Alert("Error Saving offer changes!");
    }
});

I hope this is helpful.

Senad Meškin
  • 13,597
  • 4
  • 37
  • 55