How to pass a complex view model into a controller action via ajax call with JSON in .Net MVC4?

[亡魂溺海] 提交于 2019-12-12 13:05:44

问题


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.

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
  • 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.

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


回答1:


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.

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:

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:


So here's what I came up with... I included code for both a WarningNotification and an 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. all fields go under the root type (Notification in this case) so fields for specialized types are still prefixed with "Notification." - see "Notification.AlertId"

Javascript code to tie it all together:

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


来源:https://stackoverflow.com/questions/20915681/how-to-pass-a-complex-view-model-into-a-controller-action-via-ajax-call-with-jso

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!