21

Just started a new project using .NET Core. Added my Web API controller and related method. Using Postman I created a JSON object and posted it to my controller method. Bear in mind the JSON object matches the Object param in the controller method exactly.

In debug mode I can see the object, it is not null, the properties are there, HOWEVER the prop values are defaulted to their representatives types, 0 for int, etc.

I've never seen this behavior before… so I took exactly the same code and object and replicated in a MVC project with a Web API 2 controller and it works perfectly.

What am I missing, can I not POST JSON and model bind in .NET Core?

Reading this article it seems I cannot unless I send as form POST or as querystring vars which by the way works fine.

https://lbadri.wordpress.com/2014/11/23/web-api-model-binding-in-asp-net-mvc-6-asp-net-5/

JSON:

{
   "id": "4",
   "userId": "3"
   "dateOfTest": "7/13/2017"
}

Method:

[HttpPost]
[Route("test1")]
[AllowAnonymous]
public IActionResult Test(Class1 data)
{
    return Ok();
}
1
  • Just wanted to add, if you are using core 3.1 and above, you default to using the System.Text.Json for your conversions. As of now, it only serializes/deserializes properties only and so your class need to use properties instead of fields as in the answer by edincorbin. Commented Apr 6, 2020 at 14:49

6 Answers 6

37

NOTE: If you are using aspnet core 3.0, the solution can be found here. For other versions, keep reading.

You need to mark your parameter as coming from the body with the FromBody attribute like this:

[HttpPost]
[Route("test1")]
[AllowAnonymous]
public IActionResult Test([FromBody] Class1 data)
{
    return Ok();
}

You need to make sure you're using application/json as your content type from Postman:

Postman application/json

Resulting in:

json POST action

Make sure your property setters are public as well:

public class Person
{
    public String Name;
    public Int32 Age;
}
Sign up to request clarification or add additional context in comments.

5 Comments

So that is how I had it originally and the object Class1 was null
So does it work now? Will edit the answer just to clarify headers.
@MikeAnderson: Assuming you have [FromBody], the only other reason data would be null is if none of the posted names matched up with properties on Class1. The modelbinder will only instantiate the class if there's something to instantiate it with (i.e. at least one property on the class has a value).
Thanks for clarifying. I see the issue now. The object I was attempting to bind to is an EF generated object. When I created just the class without the EF added constructor and virtual prop, it works like magic lol. So I guess in order for this work in CORE I cannot use persistent models generated by EF, I have to create my own DTO or VM. Which is best practice anyways...thanks!
THANK YOU for your bold note on net core 3.0. I've literally been searching for a whole day trying to figure out the issue and nothing else had worked until that.
8

I was struggling with this too, and thought I would share one item that was necessary for me and not in the accepted answer, I needed to add {get; set;} to each attribute in my class as so:

    public class LogString{
        public string val {get; set;}
        public string data {get; set;}
    }

The rest was the same:

        [HttpPost]
        public void Post([FromBody] LogString message)
        {      
            Console.WriteLine(message.val);
        }

After adding that it started working.

2 Comments

This is the only thing that worked for me. I haven't seen anywhere in the documentation that says you need to use properties instead of member variables! This is frustrating.
Agrrr {get; set;} in one class was missing for all properties, wasted an hour! Thanks!
6

I have an ASP.Net Core 3.1 application and for me, none of these solutions worked. I lost a few hairs (and hours of work) until I found the documentation https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-3.1

In short :

  1. Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.

  2. Update the project's Startup.ConfigureServices method to call AddNewtonsoftJson. For example:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages()
            .AddNewtonsoftJson();//this is important
    }
    

That's it ! I don't need the [FromBody] attribute.

If it doesn't work, verify that your model's properties have setters (for example public string Message { get; set; }). Don't forget the setter.

Comments

2

Consider Properties instead of Fields on your binding objects

On .NET 5, I had to add {get; set;} to my public fields to turn them into properties to get them to populate.

Either the model binder in .NET 5 can't work with fields or you have to do something special to make it populate them. I don't know which. It took a few minutes to turn all my fields into properties, so I wasn't going to spend time trying to figure out how to configure it to work with fields.

Note: If you have this problem with request objects, you will probably also have the same issue with response objects.

1 Comment

Converted to .NET 5 and spent 2 hours understanding why things do not work any more. {get; set;} was a culprit in one class. Thanks.
1

I have been struggling with this issue for several hours and none of existing solutions worked for me, but turned out that I had no public parameterless constructor for class of incoming instance. In this case, Class1 had only protected constructors and parameter was always null no matter what. Hope it helps somebody.

Comments

1

If anyone else is coming to this question and like me you are used to working with .Net Framework MVC you may be creating the JSON object similar to this -

 this.http.post<Stock>("/api/Stock2", { Stock: this.SelectedStock }, this.httpOptions)

and then expecting it to bound to 'Stock'

[HttpPost("Stock2")]
    public JsonResult Stock2(ConsolidatedStock Stock) {
        return this.Json(1);
    }

This will no longer work in .net core, you need to change how you build the JSON -

 this.http.post<Stock>("/api/Stock2", this.SelectedStock, this.httpOptions)

If you want to post multiple objects, you would need to create a new model to hold those objects, for example

public class StockViewModel {
    public ConsolidatedStock Stock { get; set; }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.