Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 405
Greg Avatar asked Mar 04 '20 22:03

Greg


People also ask

What is FromBody in asp net core?

[FromBody] attribute The ASP.NET Core runtime delegates the responsibility of reading the body to an input formatter. Input formatters are explained later in this article. When [FromBody] is applied to a complex type parameter, any binding source attributes applied to its properties are ignored.

Where is the FromBody attribute applied?

The [FromBody] attribute can be applied on only one primitive parameter of an action method. It cannot be applied to multiple primitive parameters of the same action method.

What is FromBody attribute?

The [FromBody] attribute which inherits ParameterBindingAttribute class is used to populate a parameter and its properties from the body of an HTTP request. The ASP.NET runtime delegates the responsibility of reading the body to an input formatter.


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.

like image 138
Greg Avatar answered Nov 04 '22 06:11

Greg