[c#] web-api POST body object always null

I'm still learning web API, so pardon me if my question sounds stupid.

I have this in my StudentController:

public HttpResponseMessage PostStudent([FromBody]Models.Student student)
{
    if (DBManager.createStudent(student) != null)
        return Request.CreateResponse(HttpStatusCode.Created, student);
    else
        return Request.CreateResponse(HttpStatusCode.BadRequest, student);
}

In order to test if this is working, I'm using Google Chrome's extension "Postman" to construct the HTTP POST request to test it out.

This is my raw POST request:

POST /api/Student HTTP/1.1
Host: localhost:1118
Content-Type: application/json
Cache-Control: no-cache

{"student": [{"name":"John Doe", "age":18, "country":"United States of America"}]}

student is supposed to be an object, but when I debug the application, the API receives the student object but the content is always null.

This question is related to c# rest asp.net-web-api

The answer is


In my case (.NET Core 3.0) I had to configure JSON serialization to resolve camelCase properties using AddNewtonsoftJson():

services.AddMvc(options =>
{
    // (Irrelevant for the answer)
})
.AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});

Do this in your Startup / Dependency Injection setup.


I've hit this problem so many times, but actually, it's quite straightforward to track down the cause.

Here's today's example. I was calling my POST service with an AccountRequest object, but when I put a breakpoint at the start of this function, the parameter value was always null. But why ?!

[ProducesResponseType(typeof(DocumentInfo[]), 201)]
[HttpPost]
public async Task<IActionResult> Post([FromBody] AccountRequest accountRequest)
{
    //  At this point...  accountRequest is null... but why ?!

    //  ... other code ... 
}

To identify the problem, change the parameter type to string, add a line to get JSON.Net to deserialize the object into the type you were expecting, and put a breakpoint on this line:

[ProducesResponseType(typeof(DocumentInfo[]), 201)]
[HttpPost]
public async Task<IActionResult> Post([FromBody] string ar)
{
    //  Put a breakpoint on the following line... what is the value of "ar" ?
    AccountRequest accountRequest = JsonConvert.DeserializeObject<AccountRequest>(ar);

    //  ... other code ...
}

Now, when you try this, if the parameter is still blank or null, then you simply aren't calling the service properly.

However, if the string does contain a value, then the DeserializeObject should point you towards the cause of the problem, and should also fail to convert your string into your desired format. But with the raw (string) data which it's trying to deserialize, you should now be able to see what's wrong with your parameter value.

(In my case, we were calling the service with an AccountRequest object which had been accidentally serialized twice !)


I had this problem in my .NET Framework Web API, because my model existed in a .NET Standard project that referenced a different version of data annotations.

Adding the ReadAsAsync line below highlighted the cause for me:

public async Task<HttpResponseMessage> Register(RegistrationDetails registrationDetails)
{
    var regDetails = await Request.Content.ReadAsAsync<RegistrationDetails>();

In my case, using postman I was sending a DateTime with invalid separators (%) so the parse failed silently. Be sure you are passing valid params to your class constructor.


Check if JsonProperty attribute is set on the fields that come as null - it could be that they are mapped to different json property-names.


I spend several hours with this issue... :( Getters and setters are REQUIRED in POST parameters object declaration. I do not recommend using simple data objects (string,int, ...) as they require special request format.

[HttpPost]
public HttpResponseMessage PostProcedure(EdiconLogFilter filter){
...
}

Does not work when:

public class EdiconLogFilter
{
    public string fClientName;
    public string fUserName;
    public string fMinutes;
    public string fLogDate;
}

Works fine when:

public class EdiconLogFilter
{
    public string fClientName { get; set; }
    public string fUserName { get; set; }
    public string fMinutes { get; set; }
    public string fLogDate { get; set; }
}

This is another issue related to invalid property values in an Angular Typescript request.

This is was related to the conversion between a Typescript number to an int(Int32) in C#. I was using Ticks (UTC milliseconds) which is larger than the signed, Int32 range (int in C#). Changed the C# model from int to long and everything worked fine.


If this is because Web API 2 ran into a deserialization problem due to mismatched data types, it's possible to find out where it failed by inspecting the content stream. It will read up until it hits an error, so if you read the content as a string, you should have the back half of the data you posted:

string json = await Request.Content.ReadAsStringAsync();

Fix that parameter, and it should make it further next time (or succeed if you're lucky!)...


This is a little old one and my answer will go down to the last place but even so I would like to share my experience.

Tried every suggestion but still having the same "null" value in a PUT [FromBody].

Finally found it was all about Date format while JSON serializing the EndDate property of my Angular Object.

No error was thrown, just received an empty FromBody object....


I used HttpRequestMessage and my problem got solved after doing so much research

[HttpPost]
public HttpResponseMessage PostProcedure(HttpRequestMessage req){
...
}

Seems like there can be many different causes of this problem...

I found that adding an OnDeserialized callback to the model class caused the parameter to always be null. Exact reason unknown.

using System.Runtime.Serialization;

// Validate request
[OnDeserialized]  // TODO: Causes parameter to be null
public void DoAdditionalValidatation() {...}

Just one more thing to look at... my model was marked as [Serializable] and that was causing the failure.


If the any of values of the request's JSON object are not the same type as expected by the service then the [FromBody] argument will be null.

For example, if the age property in the json had a float value:

"age":18.0

but the API service expects it to be an int

"age":18

then student will be null. (No error messages will be sent in the response unless no null reference check).


If using Postman, make sure that:

  • You have set a "Content-Type" header to "application/json"
  • You are sending the body as "raw"
  • You don't need to specify the parameter name anywhere if you are using [FromBody]

I was stupidly trying to send my JSON as form data, duh...


Just to add my history to this thread. My model:

public class UserProfileResource
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }

    public UserProfileResource()
    {
    }
}

The above object couldn't be serialized in my API Controller and would always return null. The issue was with Id of type Guid: everytime I passed empty string as an Id (being naive that it will automatically be converted to Guid.Empty) from my frontend I received null object as [FromBody] paramether.

Solution was either to

  • pass valid Guid value
  • or change Guid to String

I was looking for a solution to my problem for some minutes now, so I'll share my solution.

When you have a custom constructor within your model, your model also needs to have an empty/default constructor. Otherwise the model can't be created, obviously. Be careful while refactoring.


TL;DR: Don't use [FromBody], but roll your own version of it with better error handling. Reasons given below.

Other answers describe many possible causes of this problem. However, the root cause is that [FromBody] simply has terrible error handling, which makes it almost useless in production code.

For example, one of the most typical reasons for the parameter to be null is that the request body has invalid syntax (e.g., invalid JSON). In this case, a reasonable API would return 400 BAD REQUEST, and a reasonable web framework would do this automatically. However, ASP.NET Web API is not reasonable in this regard. It simply sets the parameter to null, and the request handler then needs "manual" code to check if the parameter is null.

Many of the answers given here are therefore incomplete with regards to error handling, and a buggy or malicious client may cause unexpected behavior on the server side by sending an invalid request, which will (in the best case) throw a NullReferenceException somewhere and return an incorrect status of 500 INTERNAL SERVER ERROR or, worse, do something unexpected or crash or expose a security vulnerability.

A proper solution would be to write a custom "[FromBody]" attribute which does proper error handling and returns proper status codes, ideally with some diagnostic information to aid client developers.

A solution that might help (not tested yet) is to make parameters required, as follows: https://stackoverflow.com/a/19322688/2279059

The following clumsy solution also works:

// BAD EXAMPLE, but may work reasonably well for "internal" APIs...

public HttpResponseMessage MyAction([FromBody] JObject json)
{
  // Check if JSON from request body has been parsed correctly
  if (json == null) {
    var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
      ReasonPhrase = "Invalid JSON"
    };
    throw new HttpResponseException(response);
  }

  MyParameterModel param;
  try {
    param = json.ToObject<MyParameterModel>();
  }
  catch (JsonException e) {
    var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
      ReasonPhrase = String.Format("Invalid parameter: {0}", e.Message)
    };
    throw new HttpResponseException(response);
  }

  // ... Request handling goes here ...

}

This does (hopefully) proper error handling, but is less declarative. If, for example, you use Swagger to document your API, it will not know the parameter type, which means you need to find some manual workaround to document your parameters. This is just to illustrate what [FromBody] should be doing.

EDIT: A less clumsy solution is to check ModelState: https://stackoverflow.com/a/38515689/2279059

EDIT: It appears that ModelState.IsValid is not, as one would expect, set to false if using JsonProperty with Required = Required.Always and a parameter is missing. So this is also useless.

However, in my opinion, any solution that requires writing additional code in every request handler is unacceptable. In a language like .NET, with powerful serialization capabilities, and in a framework like ASP.NET Web API, request validation should be automatic and built-in, and it is totally doable, even though Microsoft does not provide the necessary built-in tools.


None of the above was my solution: in my case the issue is that [ApiController] was not added to the controller so it is giving Null value

[Produces("application/json")]
[Route("api/[controller]")]
[ApiController] // This was my problem, make sure that it is there!
public class OrderController : Controller

...


Maybe for someone it will be helpful: check the access modifiers for your DTO/Model class' properties, they should be public. In my case during refactoring domain object internals were moved to DTO like this:

// Domain object
public class MyDomainObject {
    public string Name { get; internal set; }
    public string Info { get; internal set; }
}
// DTO
public class MyDomainObjectDto {
    public Name { get; internal set; } // <-- The problem is in setter access modifier (and carelessly performed refactoring).
    public string Info { get; internal set; }
}

DTO is being finely passed to client, but when the time comes to pass the object back to the server it had only empty fields (null/default value). Removing "internal" puts things in order, allowing deserialization mechanizm to write object's properties.

public class MyDomainObjectDto {
    public Name { get; set; }
    public string Info { get; set; }
}

I just ran into this and was frustrating. My setup: The header was set to Content-Type: application/JSON and was passing the info from the body with JSON format, and was reading [FromBody] on the controller.

Everything was set up fine and I expect it to work, but the problem was with the JSON sent over. Since it was a complex structure, one of my classes which was defined 'Abstract' was not getting initialized and hence the values weren't assigned to the model properly. I removed the abstract keyword and it just worked..!!!

One tip, the way I could figure this out was to send data in parts to my controller and check when it becomes null... since it was a complex model I was appending one model at a time to my request params. Hope it helps someone who runs into this stupid issue.


After Three days of searching and none of above solutions worked for me , I found another approach to this problem in this Link: HttpRequestMessage

I used one of the solutions in this site

[HttpPost]
public async System.Threading.Tasks.Task<string> Post(HttpRequestMessage request)
{
    string body = await request.Content.ReadAsStringAsync();
    return body;
}

I had the same problem.

In my case, the problem was in public int? CreditLimitBasedOn { get; set; } property I had.

my JSON had the value "CreditLimitBasedOn":true when It should contain an integer. This property prevented the whole object being deserialized on my api method.


It can be helpful to add TRACING to the json serializer so you can see what's up when things go wrong.

Define an ITraceWriter implementation to show their debug output like:

class TraceWriter : Newtonsoft.Json.Serialization.ITraceWriter
{
    public TraceLevel LevelFilter {
        get {
            return TraceLevel.Error;
        }
    }

    public void Trace(TraceLevel level, string message, Exception ex)
    {
        Console.WriteLine("JSON {0} {1}: {2}", level, message, ex);
    }
}

Then in your WebApiConfig do:

    config.Formatters.JsonFormatter.SerializerSettings.TraceWriter = new TraceWriter();

(maybe wrap it in an #if DEBUG)


I was also trying to use the [FromBody], however, I was trying to populate a string variable because the input will be changing and I just need to pass it along to a backend service but this was always null

Post([FromBody]string Input]) 

So I changed the method signature to use a dynamic class and then convert that to string

Post(dynamic DynamicClass)
{
   string Input = JsonConvert.SerializeObject(DynamicClass);

This works well.


In my case the problem was the DateTime object I was sending. I created a DateTime with "yyyy-MM-dd", and the DateTime that was required by the object I was mapping to needed "HH-mm-ss" aswell. So appending "00-00" solved the problem (the full item was null because of this).


Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to rest

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Returning data from Axios API Access Control Origin Header error using Axios in React Web throwing error in Chrome JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value How to send json data in POST request using C# How to enable CORS in ASP.net Core WebAPI RestClientException: Could not extract response. no suitable HttpMessageConverter found REST API - Use the "Accept: application/json" HTTP Header 'Field required a bean of type that could not be found.' error spring restful API using mongodb MultipartException: Current request is not a multipart request

Examples related to asp.net-web-api

Entity Framework Core: A second operation started on this context before a previous operation completed FromBody string parameter is giving null How to read request body in an asp.net core webapi controller? JWT authentication for ASP.NET Web API Token based authentication in Web API without any user interface Web API optional parameters How do I get the raw request body from the Request.Content object using .net 4 api endpoint How to use a client certificate to authenticate and authorize in a Web API HTTP 415 unsupported media type error when calling Web API 2 endpoint The CodeDom provider type "Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider" could not be located