如何通过在.Net MVC4中使用JSON调用将复杂视图模型传递到控制器操作中?

时间:2022-10-18 09:37:34

So I scoured Stack Overflow as much as I possibly could and couldn't find an answer to this specific issue. Apologies if this has already been asked.

所以我尽可能多地搜索Stack Overflow并找不到这个特定问题的答案。如果已经有人问过,请道歉。

I've found answers to:

我找到了答案:

  • how to pass an object/class to an action
  • 如何将对象/类传递给动作
  • how to pass an object via the query string to an action
  • 如何通过查询字符串将对象传递给操作
  • how to pass an object via json to an action
  • 如何通过json将对象传递给动作
  • how to pass a polymorphic object to an action and have a custom model binder process it
  • 如何将多态对象传递给一个动作并让一个自定义模型绑定器处理它

Suppose you have the following code, how can you combine the above techniques into one solution. So I'd like to hit the action on the controller (using the jquery ajax call) with a json object, pass in a view model to the action, and have it determine the the correct polymorphic type (in this case, type of Notification) - possibly by using a custom model binder.

假设您有以下代码,如何将上述技术组合到一个解决方案中。所以我想在控制器上执行操作(使用jquery ajax调用)和json对象,将视图模型传递给操作,并让它确定正确的多态类型(在这种情况下,通知类型) ) - 可能通过使用自定义模型绑定器。

Note: this is example code used to illustrate the issue.

注意:这是用于说明问题的示例代码。

Models:

楷模:

public class Notification
{
    public int ID { get; set; }
    public string ApplicationID { get; set; }
    public string Description { get; set; }
    public System.DateTime DateStamp { get; set; }
}

public class WarningNotification : Notification
{
    public string WarningText { get; set; }
}

public class AlertNotification : Notification
{
    public string AlertText { get; set; }
    public int AlertId { get; set; }
}

View Model:

查看型号:

public class SaveNotificationViewModel
{
    public Notification Notification { get; set; }
    public string Hash { get; set; }
    public List<int> Users { get; set; }
}

Controller action:

控制器动作:

public ActionResult Save(SaveNotificationViewModel model)
{
    //code inside method irrelevant...
}

Model Binder:

型号粘合剂:

public class NoticationModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
        var type = Type.GetType((string)typeValue.ConvertTo(typeof(string)), true);

        if (!typeof(Notification).IsAssignableFrom(type))
        {
            throw new InvalidCastException("Bad type");
        }

        var model = Activator.CreateInstance(type);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
        return model;
    }
}

Source of initial question that sent me down this rabbit hole and code largely borrowed from: Methodology for passing a mix of: list<>, object and primitives to an ASP MVC controller action

最初问题的来源让我误解了这个兔子洞和代码很大程度上借鉴了:方法论将传递混合:list <>,对象和基元传递给ASP MVC控制器动作

2 个解决方案

#1


4  

So I'd like to hit the action on the controller (using the jquery ajax call) with a json object, pass in a view model to the action, and have it determine the the correct polymorphic type (in this case, type of Notification) - possibly by using a custom model binder.

所以我想在控制器上执行操作(使用jquery ajax调用)和json对象,将视图模型传递给操作,并让它确定正确的多态类型(在这种情况下,通知类型) ) - 可能通过使用自定义模型绑定器。

I'd recommend NOT using a single action. First, although easy to implement, custom model binding combined with an action that takes a base type or interface typically becomes harder to debug, harder to unit test, and anyone who looks at the controller action can't figure out what is happening only by looking at the action itself.

我建议不要使用单一动作。首先,虽然易于实现,但自定义模型绑定与采用基本类型或接口的操作相结合通常变得更难调试,更难以进行单元测试,并且任何查看控制器操作的人都无法弄清楚发生了什么看着行动本身。

I'd highly recommend creating action that are specific to the request being made.

我强烈建议您创建特定于请求的操作。

I dummied these down to relevant code for the example.

我将这些问题简化为相关代码。

public ProcessNotify(Notification model)
{
  this.SharedNotifyCode(model);

  // specific Notification only code
}

public ProcessWarningNotification(WarningNotification model)
{
  this.SharedNotifyCode(model);

  // specific WarningNotification only code
}

public ProcessAlertNotification(AlertNotification model)
{
  this.SharedNotifyCode(model);

  // specific AlertNotification only code
}

private SharedNotifyCode(Notification notification)
{
  // shared code
}

This is easy to maintain, debug, test and the code itself is Self-Documenting.

这很容易维护,调试,测试,代码本身就是自我记录。

The same thing can be done with the javascript (jQuery) code:

使用javascript(jQuery)代码可以完成同样的事情:

function ProcessNotify()
{
  notify = {
    ID = 1,
    ApplicationID = 2,
    Description = "description",
    DateStamp = new Date() // something like this.. forgot JS datetimes
    };

  SendNotify(notify, "ProcessNotify");
}

function ProcessWarning()
{
  notify = {
    ID = 1,
    ApplicationID = 2,
    Description = "description",
    DateStamp = new Date(), // something like this.. forgot JS datetimes
    WarningText = "Oh noes!"
    };

  SendNotify(notify, "ProcessWarningNotification");
}

function ProcessAlert()
{
  notify = {
    ID = 1,
    ApplicationID = 2,
    Description = "description",
    DateStamp = new Date(), // something like this.. forgot JS datetimes
    AlertText = "Oh noes!",
    AlertId = 3
    };

  SendNotify(notify, "ProcessAlertNotification");
}

function SendNotify(notify, action)
{
  var jqXHR = $.ajax({
    url: '/' + Controller + '/' + action,
    data: notify,
    type: "POST",
    success: function(result)
    {
    }
    // etc
  });
}

#2


0  

So here's what I came up with... I included code for both a WarningNotification and an AlertNotification.

所以这就是我提出的......我包含了WarningNotification和AlertNotification的代码。

The key for me that I had overlooked were:

我忽略的关键是:

  1. to stick "ModelType" at the root level in the JSON and not underneath the notification prefix (as in "Notification.ModelType" - this is wrong... don't do it that way).
  2. 在JSON的根级别粘贴“ModelType”而不是在通知前缀下面(如“Notification.ModelType” - 这是错误的......不要这样做)。
  3. all fields go under the root type (Notification in this case) so fields for specialized types are still prefixed with "Notification." - see "Notification.AlertId"
  4. 所有字段都在根类型下(本例中为通知),因此专用类型的字段仍以“通知”为前缀。 - 请参阅“Notification.AlertId”

Javascript code to tie it all together:

将所有内容绑定在一起的Javascript代码:

// this is an example warning notification
var data =
    {
        "Notification.Id": '21',
        "Notification.ApplicationId": 'TARDIS',
        "Notification.Description": "The most important warning notification ever!",
        "Notification.WarningText": "I gave them the wrong warning. I should have told them to run, as fast as they can. Run and hide, because the monsters are coming - the human race.",
        "ModelType": 'Models.WarningNotification',
        "Hash": '27ad5218-963a-4df8-8c90-ee67c5ba9f30'
    };

// this is an example alert notification
var data =
    {
        "Notification.Id": '21',
        "Notification.ApplicationId": 'TARDIS',
        "Notification.Description": "The most important alert notification ever!",
        "Notification.AlertText": "Rose,before I go, I just want to tell you, you were fantastic. Absolutely fantastic. And you know what....so was I.",
        "Notification.AlertId": "1024",
        "ModelType": 'Models.AlertNotification',
        "Hash": '27ad5218-963a-4df8-8c90-ee67c5ba9f32'
    };

$.ajax({
    type: 'POST',
    url: '/notification/save',
    dataType: 'json',
    data: data,
    success: function (result) {
        console.log('SUCCESS:');
        console.log(result);
    },
    error: function (xhr, ajaxOptions, thrownError) {
        console.log('ERROR:');
        console.log(thrownError);
    }
});

#1


4  

So I'd like to hit the action on the controller (using the jquery ajax call) with a json object, pass in a view model to the action, and have it determine the the correct polymorphic type (in this case, type of Notification) - possibly by using a custom model binder.

所以我想在控制器上执行操作(使用jquery ajax调用)和json对象,将视图模型传递给操作,并让它确定正确的多态类型(在这种情况下,通知类型) ) - 可能通过使用自定义模型绑定器。

I'd recommend NOT using a single action. First, although easy to implement, custom model binding combined with an action that takes a base type or interface typically becomes harder to debug, harder to unit test, and anyone who looks at the controller action can't figure out what is happening only by looking at the action itself.

我建议不要使用单一动作。首先,虽然易于实现,但自定义模型绑定与采用基本类型或接口的操作相结合通常变得更难调试,更难以进行单元测试,并且任何查看控制器操作的人都无法弄清楚发生了什么看着行动本身。

I'd highly recommend creating action that are specific to the request being made.

我强烈建议您创建特定于请求的操作。

I dummied these down to relevant code for the example.

我将这些问题简化为相关代码。

public ProcessNotify(Notification model)
{
  this.SharedNotifyCode(model);

  // specific Notification only code
}

public ProcessWarningNotification(WarningNotification model)
{
  this.SharedNotifyCode(model);

  // specific WarningNotification only code
}

public ProcessAlertNotification(AlertNotification model)
{
  this.SharedNotifyCode(model);

  // specific AlertNotification only code
}

private SharedNotifyCode(Notification notification)
{
  // shared code
}

This is easy to maintain, debug, test and the code itself is Self-Documenting.

这很容易维护,调试,测试,代码本身就是自我记录。

The same thing can be done with the javascript (jQuery) code:

使用javascript(jQuery)代码可以完成同样的事情:

function ProcessNotify()
{
  notify = {
    ID = 1,
    ApplicationID = 2,
    Description = "description",
    DateStamp = new Date() // something like this.. forgot JS datetimes
    };

  SendNotify(notify, "ProcessNotify");
}

function ProcessWarning()
{
  notify = {
    ID = 1,
    ApplicationID = 2,
    Description = "description",
    DateStamp = new Date(), // something like this.. forgot JS datetimes
    WarningText = "Oh noes!"
    };

  SendNotify(notify, "ProcessWarningNotification");
}

function ProcessAlert()
{
  notify = {
    ID = 1,
    ApplicationID = 2,
    Description = "description",
    DateStamp = new Date(), // something like this.. forgot JS datetimes
    AlertText = "Oh noes!",
    AlertId = 3
    };

  SendNotify(notify, "ProcessAlertNotification");
}

function SendNotify(notify, action)
{
  var jqXHR = $.ajax({
    url: '/' + Controller + '/' + action,
    data: notify,
    type: "POST",
    success: function(result)
    {
    }
    // etc
  });
}

#2


0  

So here's what I came up with... I included code for both a WarningNotification and an AlertNotification.

所以这就是我提出的......我包含了WarningNotification和AlertNotification的代码。

The key for me that I had overlooked were:

我忽略的关键是:

  1. to stick "ModelType" at the root level in the JSON and not underneath the notification prefix (as in "Notification.ModelType" - this is wrong... don't do it that way).
  2. 在JSON的根级别粘贴“ModelType”而不是在通知前缀下面(如“Notification.ModelType” - 这是错误的......不要这样做)。
  3. all fields go under the root type (Notification in this case) so fields for specialized types are still prefixed with "Notification." - see "Notification.AlertId"
  4. 所有字段都在根类型下(本例中为通知),因此专用类型的字段仍以“通知”为前缀。 - 请参阅“Notification.AlertId”

Javascript code to tie it all together:

将所有内容绑定在一起的Javascript代码:

// this is an example warning notification
var data =
    {
        "Notification.Id": '21',
        "Notification.ApplicationId": 'TARDIS',
        "Notification.Description": "The most important warning notification ever!",
        "Notification.WarningText": "I gave them the wrong warning. I should have told them to run, as fast as they can. Run and hide, because the monsters are coming - the human race.",
        "ModelType": 'Models.WarningNotification',
        "Hash": '27ad5218-963a-4df8-8c90-ee67c5ba9f30'
    };

// this is an example alert notification
var data =
    {
        "Notification.Id": '21',
        "Notification.ApplicationId": 'TARDIS',
        "Notification.Description": "The most important alert notification ever!",
        "Notification.AlertText": "Rose,before I go, I just want to tell you, you were fantastic. Absolutely fantastic. And you know what....so was I.",
        "Notification.AlertId": "1024",
        "ModelType": 'Models.AlertNotification',
        "Hash": '27ad5218-963a-4df8-8c90-ee67c5ba9f32'
    };

$.ajax({
    type: 'POST',
    url: '/notification/save',
    dataType: 'json',
    data: data,
    success: function (result) {
        console.log('SUCCESS:');
        console.log(result);
    },
    error: function (xhr, ajaxOptions, thrownError) {
        console.log('ERROR:');
        console.log(thrownError);
    }
});