ASP.NET Core [FromBody] vs MVC 5 binding

Patrick Laramee

I got an MVC 5 application that i'm porting to asp.net Core.

In the MVC application call to controller we're made using AngularJS $resource (sending JSON) and we we're POSTing data doing :

ressource.save({ entries: vm.entries, projectId: vm.project.id }).$promise...

that will send a JSON body like:

{
  entries: 
  [
    {
      // lots of fields
    }
  ],
  projectId:12
}

the MVC controller looked like this :

[HttpPost]
public JsonResult Save(List<EntryViewModel> entries, int projectId) {
// code here
}

How can I replicate the same behaviour with .NET Core since we can't have multiple [FromBody]

Patrick Laramee

It's still rough but I made a Filter to mimic the feature.

public class OldMVCFilter : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.HttpContext.Request.Method != "GET")
        {
            var body = context.HttpContext.Request.Body;
            JToken token = null;
            var param = context.ActionDescriptor.Parameters;

            using (var reader = new StreamReader(body))
            using (var jsonReader = new JsonTextReader(reader))
            {
                jsonReader.CloseInput = false;
                token = JToken.Load(jsonReader);
            }
            if (token != null)
            {
                var serializer = new JsonSerializer();
                serializer.DefaultValueHandling = DefaultValueHandling.Populate;
                serializer.FloatFormatHandling = FloatFormatHandling.DefaultValue;

                foreach (var item in param)
                {
                    JToken model = token[item.Name];

                    if (model == null)
                    {
                        // try to cast the full body as the current object
                        model = token.Root;
                    }

                    if (model != null)
                    {
                        model = this.RemoveEmptyChildren(model, item.ParameterType);
                        var res = model.ToObject(item.ParameterType, serializer);
                        context.ActionArguments[item.Name] = res;
                    }
                }
            }
        }
    }
    private JToken RemoveEmptyChildren(JToken token, Type type)
    {
        var HasBaseType = type.GenericTypeArguments.Count() > 0;
        List<PropertyInfo> PIList = new List<PropertyInfo>();
        if (HasBaseType)
        {
            PIList.AddRange(type.GenericTypeArguments.FirstOrDefault().GetProperties().ToList());
        }
        else
        {
            PIList.AddRange(type.GetTypeInfo().GetProperties().ToList());
        }

        if (token != null)
        {
            if (token.Type == JTokenType.Object)
            {
                JObject copy = new JObject();
                foreach (JProperty jProp in token.Children<JProperty>())
                {
                    var pi = PIList.FirstOrDefault(p => p.Name == jProp.Name);
                    if (pi != null) // If destination type dont have this property we ignore it
                    {
                        JToken child = jProp.Value;
                        if (child.HasValues)
                        {
                            child = RemoveEmptyChildren(child, pi.PropertyType);
                        }
                        if (!IsEmpty(child))
                        {
                            if (child.Type == JTokenType.Object || child.Type == JTokenType.Array)
                            {
                                // nested value has been checked, we add the object
                                copy.Add(jProp.Name, child);
                            }
                            else
                            {
                                if (!pi.Name.ToLowerInvariant().Contains("string"))
                                {
                                    // ignore empty value when type is not string
                                    var Val = (string)child;
                                    if (!string.IsNullOrWhiteSpace(Val))
                                    {
                                        // we add the property only if it contain meningfull data
                                        copy.Add(jProp.Name, child);
                                    }
                                }
                            }
                        }
                    }
                }
                return copy;
            }
            else if (token.Type == JTokenType.Array)
            {
                JArray copy = new JArray();
                foreach (JToken item in token.Children())
                {
                    JToken child = item;
                    if (child.HasValues)
                    {
                        child = RemoveEmptyChildren(child, type);
                    }
                    if (!IsEmpty(child))
                    {
                        copy.Add(child);
                    }
                }
                return copy;
            }
            return token;
        }
        return null;
    }

    private bool IsEmpty(JToken token)
    {
        return (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined);
    }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

ASP.NET Core MVC Mixed Route/FromBody Model Binding & Validation

FromBody is null next migrating to ASP .Net Core MVC 3.1

In ASP.Net Core 5 MVC Controller, when passed a JSON object FromBody that contains a decimal the model is always null

ASP.NET MVC 5 ViewModel Binding

ASP.NET Core FromBody Model Binding: Bind a Class with Interafece Field

Model binding / ViewModel binding in ASP.Net Core MVC

How to get the ASP.NET Core 2.0 MVC Model Binder to bind complex types without FromBody attributes

Unable to call API Post Method that has a class parameter with [FromBody] attribute in asp.net core mvc

Asp.net core blazor vs .net core mvc with razor

Handling Model Binding Errors when using [FromBody] in .NET Core 2.1

Enum model binding [FromRoute] behaves differently than [FromBody] in .net core

Dynamically binding models at runtime in ASP.NET Core MVC

ASP.NET Core MVC - Binding to model at runtime

How to debug ASP.NET Core MVC data binding?

Binding to a TimeSpan property in Asp.NET MVC Core

binding a Guid parameter in asp.net mvc core

Problem binding DateOnly in ASP.NET Core 6 MVC

Model Binding properties null on asp.net core 3 MVC

ASP.NET Core 3 MVC : model binding of a list of objects

Model binding with multiple rows in ASP.NET Core MVC

Asp.Net core mvc - not binding to fresh model during postback

ASP.NET Core Razor pages vs Full MVC Core

Convention based binding in ASP.NET 5 / MVC 6

Binding async method into DropDownList ASP.NET MVC 5

ASP.NET MVC 5 - Model Binding Not Working

ASP.NET MVC 5 model binding list is empty

Normalize string inputs with Binding ASP.Net Core 5

Localization and Globalization in ASP.NET Core MVC (.NET 5)

ASP.NET core - how to pass optional [FromBody] parameter?