40

I've got a new API that I'm building with ASP.NET Core, and I can't get any data POST'ed to an endpoint.

Here's what the endpoint looks like:

[HttpPost]
[Route("StudentResults")]
public async Task<IActionResult> GetStudentResults([FromBody]List<string> userSocs, [FromBody]int collegeId)
{
    var college = await _collegeService.GetCollegeByID(collegeId);
    // var occupations = await _laborMarketService.GetOccupationProgramsBySocsAndCollege(userSocs, college);
    return Ok();
}

And here's what my payload that I'm sending through Postman looks like:

{
    "userSocs": [
            "291123",
            "291171",
            "312021",
            "291071",
            "152031",
            "533011"
        ],
    "collegeId": 1
}

I'm making sure that I have postman set as a POST, with Content-Type application/json. What am I doing wrong?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Alex Kibler
  • 4,674
  • 9
  • 44
  • 74

10 Answers10

55

You get always null because you need to encapsulate all your post variables inside only one object. Like this:

public class MyPostModel {
    public List<string> userSocs {get; set;}
    public int collegeId {get; set;}
}

and then

public async Task<IActionResult> GetStudentResults([FromBody] MyPostModel postModel)
Tinwor
  • 7,765
  • 6
  • 35
  • 56
  • That wasn't an issue in .NET 4.5, was it? I could swear I remember sending multiple params in a POST – Alex Kibler Feb 16 '17 at 15:21
  • 6
    @AlexKibler: Only if you send the parameters via form or get query. You can only have a single model in your body, so any non-basic type (int, string, etc.) will be serialized to the first model. In ASP.NET Core (indepentent of 4.5 or .NET Core) you can only have a single FromBody (in WebApi 2.x it was implicit), because WebAPI and MVC are now merged into a single framework, where they were distinct ones previously – Tseng Feb 16 '17 at 16:08
29

If the model is null, check:

1) Where the data is sent: body, form? and based on that add the decorator to the action. For ex:

[HttpPost]
public JsonResult SaveX([FromBody]MyVM vm) { ... }

2) Check ModelState: if it's invalid the vm will not be bound so it will be null.

if (ModelState.IsValid) { ... }
Piotr Kula
  • 9,597
  • 8
  • 59
  • 85
Francisco Goldenstein
  • 13,299
  • 7
  • 58
  • 74
  • 4
    I spent many hours debugging why I'm getting null FormData, all was good but my request data was invalid. So my two cents, Please add `if (!ModelState.IsValid) { ... }` at the first line of your method to check that you have the right model first :) First things first – Mo Zaatar Aug 14 '18 at 01:25
  • 2
    Correct, that's what I described on the second point. In fact, you could create an action filter that checks Model.IsValid and if it's not valid return a validation message to the client. That way you don't have to do it every single action. – Francisco Goldenstein Aug 17 '18 at 12:26
  • 5
    The ModelState.IsValid is a great troubleshooting suggestion! – Warren Schwartz Aug 02 '19 at 08:23
  • my man, i've been scratching my head on this half day. turns out ModelState was Invalid. Thanks brother! – Irshu Jul 11 '21 at 07:51
21

Another reason for the model binding to fail (always null) is if the data type for a property doesn't match. For example here is a simple model:

public class MyService {
    public string JobId { get; set; }
    public int ServiceType {get; set;}
}

And here is some json that doesn't match:

{"JobId":1, "ServiceType":1}

I got caught with this when I was retrieving the JobId using jquery's .data function, it was automatically converting it to an int. Fixed it by using .attr function instead.

gsxrboy73
  • 1,382
  • 14
  • 19
  • 1
    You saved the day with this answer. I have an int field that is getting sent as a string by axios (e.g. "99" instead of 99). It's bizarre to me that the binding system doesn't create the object and bind the fields it can. Seems far easier to debug that way. You would also think it could do a conversion between certain data types. – Ben Mills Dec 15 '20 at 16:02
4

Also, make sure those variables inside your parameter class are declared as Public, (or they'll just keep returning as null)..

Kingsley
  • 121
  • 6
3

If you want to send two or more models, you should use this example:

[HttpPost]
public async Task<ActionResult> addUsuario([FromBody] Newtonsoft.Json.Linq.JObject datos)
{
    Usuarios user = datos["usuario"].ToObject<Usuarios>();
    Empresas empresa = datos["empresa"].ToObject<Empresas>();
    return Json(await _srv.addUsuario(user, empresa));
}
2

I know it is not related to your case, still, I am posting my answer here.

It is a silly mistake that I had done in my code. I just copied one of my Get requests and changed it to a Post request, and forgot to decorate the parameter with [FromBody]. If anyone else is having the same problem, please make sure that you are decorating the parameter with [FromBody].

[HttpPost]
public IApiResponse Update([FromBody] User user) {
    if (user == null) return new ApiBadRequestResponse(ModelState);
    return _userService.Post(user) ? new ApiOkResponse(user) : new ApiResponse(500);
}
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Sibeesh Venu
  • 18,755
  • 12
  • 103
  • 140
2

Assuming the [FromBody] class is made up of primitive data types;

  1. [FromBody] is public
  2. [FromBody] has an empty constructor ()
  3. [FromBody] is serializable.
Juls
  • 658
  • 6
  • 15
2

Make sure that your data transfer object has "Public" as Access modifier and also your oject properties have getter and setter methods.

MaybeNextTime
  • 561
  • 5
  • 11
1

For me, all values were coming in as null because my object had an enum that was not being parsed. If you have having this problem, I'd recommend turning on enum parsing at the Startup.cs per this SO

Negatar
  • 637
  • 8
  • 14
0

In my case it was using Newtonsoft.Json in my body object with JsonProperty attributes for some reason. Changing in to System.Text.Json.Serialization and JsonPropertyName attribute solved the problem

Tropin Alexey
  • 606
  • 1
  • 7
  • 16