I\'m still learning web API, so pardon me if my question sounds stupid.
I have this in my StudentController
:
public HttpResponseMessage
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.
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.
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.
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; }
}
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;
}
If using Postman, make sure that:
I was stupidly trying to send my JSON as form data, duh...