ASP.NET Core [FromBody]与MVC 5绑定

时间:2018-10-29 20:27:23

标签: asp.net-mvc asp.net-core asp.net-core-mvc

我有一个要移植到asp.net Core的MVC 5应用程序。

在MVC应用程序对控制器的调用中,我们是使用AngularJS $ resource(发送JSON)完成的,并且我们正在发布数据:

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

将发送如下的JSON正文:

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

MVC控制器看起来像这样:

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

由于我们不能有多个[FromBody],因此如何使用.NET Core复制相同的行为

3 个答案:

答案 0 :(得分:2)

如评论中所指出,一种可能的解决方案是将要发布的属性统一到单个模型类上。

如下所示的方法可以解决问题:

public class SaveModel
{
    public List<EntryViewModel> Entries{get;set;}

    public int ProjectId {get;set;}
}

别忘了用[FromBody]属性装饰模型:

[HttpPost]
public JsonResult Save([FromBody]SaveViewModel model) 
{
    // code here
}

希望这会有所帮助!

答案 1 :(得分:2)

在行动方法中,FromBody外观不能有多个参数。如果需要,请使用复杂的类型,例如具有与参数等效的属性的类或类似的动态类型

[HttpPost("save/{projectId}")]
public JsonResult Save(int projectId, [FromBody] dynamic entries) {
// code here
}

答案 2 :(得分:0)

它仍然很粗糙,但是我做了一个筛选器来模仿该功能。

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);
    }
}