When posting to an ASP.NET Core 3.1 web app, "[FromBody]MyClass data" is often null

Viewed 2756

I encountered this interesting (frustrating) problem recently after upgrading from .NET Core 2.2 to 3.1. Previously I would POST data to the web app, with the receiving method looking something like this:

public IActionResult OnPostAddNewDataAsync([FromBody]MyClass data)
{
    //...
}

where MyClass might be something like this:

public class MyClass
{
    public string Field1 {get; set;}
    public integer Value1 {get; set;}
}

(For reference, to hit the endpoint from Javascript, you can do it like this:

 await PostDataAsync("AddNewData", {
      "Field1": "Hello I am",
      "Value1": 7
 });

async function PostDataAsync(handler, data)
{
    return await $.ajax({
        url: window.location.pathname + "?handler=" + handler,
        type: "POST",
        contentType: "application/json",
        headers:
        {
            RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val()
        },
        data: JSON.stringify(data)
    });
}

it took me a while to figure that out!).

This worked fine.

After updating .NET Core 3.1, many of my POST methods stopped working, and the [FromBody] value would be null.

Note: As part of my upgrade to .NET Core 3.1 I removed references to Newtonsoft.Json, deciding instead to try and use the new System.Text.Json. This would turn out to be important!

1 Answers

As mentioned, references to Newtonsoft.Json were removed, leaving any automatic Json conversion to be dealt with by System.Text.Json. It turns out that this was the cause of the problem as System.Text.Json is not as flexible (Microsoft themselves say so, and that it is intended to be so: How to migrate from Newtonsoft.Json to System.Text.Json).

Before .NET Core 3, ASP.NET Core used Newtonsoft.Json internally, and now it uses System.Text.Json instead.

An example is with a numeric field, e.g MyClass.Value1. If from Javascript you pass "10" or 10, Newtonsoft.Json will cope with that and recognise both as 10. With System.Text.Json by default that field cannot have surrounding quotes, and if in the Json you posted it did you'd be getting a null [FromBody] value.

The quickest solution to this problem is to revert to Newtonsoft.Json for these cases, and that is easily done by:

  • installing the Nuget package Microsoft.AspNetCore.Mvc.NewtonsoftJson
  • in Startup.cs, update the following method as so:

.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages().AddNewtonsoftJson();

    //All your other code:
    //...
}

This is taken directly from here.

Having made this change everything worked as expected again.

I am aware that System.Text.Json can be configured with custom parsers to handle these kind of situations (which I do use elsewhere), but I have dozens of POST methods and rather than updating all of them to work with the new way, it was a lot easier to do as described above.

Related