Search code examples
c#curlasp.net-web-api2dotnet-httpclient

WebApi2 Controller Post method always receiving null FromBody


I'm having issues with the [FromBody] in my POST method always being null.

I've reproduced it using a minimal app in VS2019:

  • Create project ASP.NET Web Application (.NET Framework)
  • Select Web API

This creates a skeleton project with a ValuesController class with the following method:

public void Post([FromBody] string value)
{
}

I run up this project in "IIS Express (Google Chrome)" and browse to https://localhost:44358/api/values which works fine and breakpoints in the Get method are hit.

I then try and POST data using cUrl:

curl --header "Content-Type: application/json" --url https://localhost:44358/api/values --data "wibble"

The breakpoint is hit in VS2019, but the value is null (I expected "wibble").

I can see in the request content that the length is 6, and this changes based on how long the string I pass into the data is, but calls to read it return empty strings, or a byte[] of zeros.

this.Request.Content.ReadAsStringAsync().Result     ""
this.Request.Content.Headers.ContentType            {application/json}
this.Request.Content.Headers.ContentLength          6

Attempting to POST from another C# app using a HttpClient also shows the same results.

What am I missing? The fact that the issue occurs in the default skeleton app, and everyone isn't complaining about it suggests it must be something obvious, but...

UPDATE

I'm seeing the same problem in Fiddler, but have got it to work with the HttpClient using:

client.PostAsync<string>(url, "wibble", new JsonMediaTypeFormatter()).Result;

But the following doesn't work (received as null):

client.PostAsync(url, new StringContent("wibble", Encoding.UTF8), new JsonMediaTypeFormatter()).Result;

Solution

  • Changing the generated code from:

    public void Post([FromBody] string value) { ... }
    

    to:

    public void Post([FromBody] MyClass value) { ... }
    

    Now works using a standard cUrl / Fiddler / Javascript / etc POST.

    So I guess the issue is with attempting to automatically convert the string into an object, which would explain why large numbers of people aren't having this issue.

    If you do want to POST text, the automatically generated controller (that looks like it should do what you want) doesn't work.

    To work around that, change the Post method to:

    public void Post()
    {
        string value = Request.Content.ReadAsStringAsync().Result;
        ...
    }
    

    Not having the [FromBody] now means that Request.Content.ReadAsStringAsync() returns the content as expected.

    In summary:

    [Route("api/values/obj")]
    public void Post([FromBody] MyObject value) /* OK */
    {
        ...
    }
    
    [Route("api/values/str")]
    public void Post([FromBody] string value) /* FAIL */
    {
        ...
    }
    
    [Route("api/values/str2")]
    public void Post() /* OK */
    {
        string value = Request.Content.ReadAsStringAsync().Result;
        ...
    }
    

    Has the following results from cUrl:

    curl "http://localhost:59801/api/Values/obj"  --header "Content-Type: application/json" --data "{id:1, name:'Andy'}"
    

    OK

    curl "http://localhost:59801/api/Values/str2" --header "Content-Type: text/plain"       --data "wibble"
    

    OK

    curl "http://localhost:59801/api/Values/str2" --header "Content-Type: application/json" --data "wobble"
    

    OK

    curl "http://localhost:59801/api/Values/str"  --header "Content-Type: text/plain"       --data "wibble"
    

    Fail. Exception 'The request entity's media type 'text/plain' is not supported for this resource.'

    curl "http://localhost:59801/api/Values/str"  --header "Content-Type: application/json" --data "wobble"
    

    Fail. Value is null.