Controller post [FromBody] Null issue when when migrating from .netcore 2.2 to 3.0

2.5k views Asked by At

I had a completely functioning program at version 2.2 when migrating to version 3.0 and replacing

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddMvc();
}

With services.AddControllers();

And replacing app.UseMvc();

With:

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

One of the controllers is broken. (Other controllers which also has Post Method and [FromBody] Works fine) The controller and the method broken is:

[Route("api/vm")]
public class MainController: Controller
{
    [HttpPost]
    [Route("Process")]
    public IActionResult GetProcess([FromBody]ProcessModel[] process)
    {
         ...
    }
}

The Model:

public class ProcessModel
{
    [JsonProperty("Name")]
    public string Name { get; set; }
    [JsonProperty("ExeName")]
    public string ExeName { get; set; }
    [JsonProperty("Path")]
    public string Path { get; set; }
    [JsonProperty("VersionPath")]
    public string VersionPath { get; set; }
    [JsonProperty("Id")]
    public string Id { get; set; }
    [JsonProperty("Status")]
    public string Status { get; set; }
    [JsonProperty("Ver")]
    public string Ver { get; set; }
    [JsonProperty("Args")]
    public string[] Args { get; set; }
    [JsonProperty("Instances")]
    public List<ProcessDetails> Instances { get; set; }
    [JsonProperty("Multiple")]
    public string Multiple { get; set; }
}  

The call I am making to /api/vm/Process:

[
    {
        "Name": "Test",
        "ExeName": "Test",
        "Multiple": false,
        "Path": "Test",
        "VersionPath": "Test",
        "Args": {
            "IsFile": false
        }
    },
    {
        "Name": "Test",
        "ExeName": "Test.exe",
        "Multiple": false,
        "Path": "Test",
        "VersionPath": "Test",
        "Args": {
            "IsFile": false
        }
    }
]

The app worked at production just fine for a few months. All I did was upgrade to .netcore 3, Now when I debug and get to the method at the controller I get null in a process variable

Note: I used this thread when the app was broken at first place Using 'UseMvc' to configure MVC is not supported while using Endpoint Routing

4

There are 4 answers

0
Roman.Pavelko On

Problem here in Multiple and Args JSON properties.
In JSON they are bool and object, but in Process model they are both strings. I am not quite sure how it could work in Core 2.2.

2
pavinan On

Auto type converting is not available in new System.Text.Json, probably for performance reasons. You should use "Newtonsoft serializer" or use a custom converter. Some of your class properties are string but you are sending bool (Multiple property), int.

Using Newtonsoft: https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#jsonnet-support

System.Text.Json Converter sample:

https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/tests/Serialization/CustomConverterTests.Int32.cs

Register your converter like this:

services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new MyInt32Converter());
});
0
Ryan On

The reason is that your ProcessModel has string[] type for Args:

public string[] Args { get; set; }

While in your json, you pass it as an object which results in the model binding null

"Args": {
        "IsFile": false
    }

Either pass Args as string[] like

"Args": ["IsFile:false"]

Or if you do want to change the json, modify Args to a Dictionary type:

[JsonProperty("Args")]      
public Dictionary<string, string> Args { get; set; }

Remember to add reference to NewtonsoftJson like others have said

services.AddControllers().AddNewtonsoftJson();
0
thus On

Step 1: Download and install the "Microsoft.AspNetCore.Mvc.NewtonsoftJson" NuGet package in Visual Studio for Mac -I use Macbook Pro.

Step 2: I have read Migrate Startup.Configure to migrate my project to .Net Core 3. Basically all you need to do is to add the following to your existing setup on the ConfigureServices() method:

public void ConfigureServices(IServiceCollection services)
{
    // update Startup.ConfigureServices to call AddNewtonsoftJson
    services.AddControllers();
    ...
    ...
    services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
            .AddNewtonsoftJson(); 
}

And then on the Configure() method add the following:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...
    ...
    app.UseRouting();
    app.UseCors();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
    });
}

These screenshots are from Microsoft:

enter image description here

enter image description here

Step 3: Remove the FromBody from the method signature. It is not needed in .Net Core 3+ anymore. So your end point should like this:

[HttpPost("Login")]
public IActionResult Login(YourRequest request) {

}

Also, I didn't add the [JsonProperty] attribute to the model properties. My request class structure is:

using System;

namespace Models.Communication.Request
{
    public class LoginRequest
    {
        public String Email { get; set; }
        public String Password { get; set; }
    }
}