【问题标题】:passing multiple objects as parameters to mvc 6 action将多个对象作为参数传递给 mvc 6 动作
【发布时间】:2017-01-29 15:26:34
【问题描述】:

我有一个运行良好的 MVC 5 项目,我需要将该项目迁移到带有 .net 核心的 MVC 6。在设法调整一切工作后,我遇到了一个问题:我的许多操作都接受超过 1 个对象作为参数。 MVC 5 使用的模型绑定器对此没有问题,但 MVC 6 似乎将 null 放在此类操作的所有参数中,我想这是 MVC 和 WebAPI 统一的一部分。我的问题是,如果没有添加另一个请求包装器对象的模型库 ,是否还有其他方法。

例如:

    [HttpPost]
    public ActionResult GetVersionData(OvlEnvironment environment, Pipeline pipeline)
    {
        BL.SetEnviromentVersion(pipeline, environment);
        return PartialView("_Version", environment);
    }

关于mvc 5项目的ajax请求中包含json数据的形式

{ "environment" : {*Data...*},
  "pipeline" : {*Data...*}
}

被接受。在 mvc 6 中,响应同一请求的两个对象似乎都是空的。
谢谢

【问题讨论】:

  • 请提供具体例子
  • 好的,添加一个例子
  • 尝试添加 [FromBody] 例如GetVersionData([FromBody] OvlEnvironment 环境,[FromBody] Pipeline pipeline)
  • 试过了,没用
  • 是的,你不能添加[FromBody]两次..

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


【解决方案1】:

您必须向 ASP.NET Core MVC 框架提示,要绑定到模型的数据可以在发布请求的正文中找到。这是通过[FromBody] attribute 完成的。

[FromBody]:使用配置的格式化程序从 请求正文。格式化程序是根据内容类型选择的 请求。

按照设计,不可能将多个参数绑定到一个 JSON 源,如 here 所述。为了避免额外的模型类,您可以使用这样的通用 JObject:

[HttpPost]
public ActionResult GetVersionData([FromBody] JObject data)
{
    var environment = data["environment"].ToObject<OvlEnvironment>();
    var pipline = data["pipeline"].ToObject<Pipeline>();

    BL.SetEnviromentVersion(pipeline, environment);
    return PartialView("_Version", environment);
}

【讨论】:

  • “我的问题是,如果没有添加另一个请求包装器对象的模型库,是否还有其他方法。”。因为我确定我不是第一个遇到这个问题的人,所以我认为有人可能有解决方案
  • 如果已将链接添加到 GitHub 问题,其中指出,无法将正文 JSON 输入绑定到多个参数对象。因此,必须寻求某种其他解决方案。通过使用 JObject(ASP.NET Core MVC 在内部使用 JSON.net,因此没有添加其他模型库),您可以避免为数据传输定义类。另一种解决方案是将多个参数组合到一个对象。
  • 我会想到一个自定义模型 binder\formatter 来处理这个问题,遗憾的是我没有时间也不想写一个,而且很遗憾这样做,因为微软已经用早期版本做了MVC 的。我的目标不是在迁移过程中更改大量代码。我认为创建强类型包装器对象实际上是一个更好的解决方案,而不是仅仅接受 JObject 的所有操作,就好像我正在使用一种松散类型的语言一样。在这两种解决方案中,我都更改了很多代码。我正在寻找一种方法来让我的控制器保持原样。如果没有,生活就糟透了
  • 你能用 JObject 进行模型验证和 xml 格式化吗?
【解决方案2】:

我知道这篇文章很旧,但我也遇到了这个问题。

我认为接受的答案是正确的,在 ASP.NET MVC5 中可以寻找 HttpRequest.InputStream,但在 ASP.NET Core 2 中不再允许这样做。因此解决方法可能是读取流一次并将解析的 JObject 存储在 ModelState (https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.modelstatedictionary?view=aspnetcore-2.1) 中。简单的测试表明这种方法是可行的。

这是这种方法的一个例子:

public class JsonMultiParameterModelBinder : IModelBinder
{
    private static IModelBinder DefaultModelBinder;
    private const string JSONKEY = "json_body";

    public JsonMultiParameterModelBinder(IModelBinder defaultModelBinder)
    {
        DefaultModelBinder = defaultModelBinder;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var httpContext = bindingContext.HttpContext;
        var stream = bindingContext.HttpContext.Request.Body;
        if (httpContext.Request.ContentType.Contains("application/json"))
        {
            JObject jobject = null;
            ModelStateEntry entry = null;
            bindingContext.ModelState.TryGetValue(JSONKEY, out entry);
            if (entry == null)
            {

                using (var readStream = new StreamReader(stream, Encoding.UTF8))
                using (var jsonReader = new JsonTextReader(readStream))
                {
                    jobject = JObject.Load(jsonReader);
                }

                bindingContext.ModelState.SetModelValue(JSONKEY, jobject, null);
            }
            else
            {
                jobject = entry.RawValue as JObject;
            }
            var jobj = jobject[bindingContext.FieldName];

            if (jobj == null)
            {
                httpContext.Response.StatusCode = 500; // error has occured
                throw new JsonReaderException(string.Format("The rootnode '{0}' is not found in the Json message.", bindingContext.ModelName));
            }

            object obj = null;

            var JSONSerializer = new JsonSerializer();

            try
            {
                obj = jobj.ToObject(bindingContext.ModelType, JSONSerializer);
                bindingContext.Model = obj;
                bindingContext.Result = ModelBindingResult.Success(obj);
                return Task.CompletedTask;
            }
            catch (Exception ex)
            {
                httpContext.Response.StatusCode = 500; // error has occured
                throw ex;
            }

        }
        return DefaultModelBinder.BindModelAsync(bindingContext);
    }
}

还有这个活页夹的提供者:

public class JsonMultiParameterModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));

        if (context.Metadata.IsComplexType)
        {
            ComplexTypeModelBinderProvider p = new ComplexTypeModelBinderProvider();

            return new JsonMultiParameterModelBinder(p.GetBinder(context));
        }
        return null;
    }
}

在 Startup.cs 中注册:

        services.AddMvc(options =>
        {
            options.ModelBinderProviders.Insert(0, new JsonMultiParameterModelBinderProvider());
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多