无法发布复杂的对象

时间:2016-06-23 20:52:46

标签: javascript jquery asp.net asp.net-mvc asp.net-mvc-5

我的观点有这个功能:

function setMessengerState() {
    var serviceURL = $("#messenger-set-state-url").val();

    var data = {
        messengerState: g_messengerState,
    };

    $.ajax({
        type: "POST",
        url: serviceURL,
        data: JSON.stringify(data),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: successFunc,
        error: errorFunc
    });

    function successFunc(data, status) {
        console.log("saved");
    }

    function errorFunc(data, status) {
        console.log("failed?");
    }
}

这是在Chrome调试器上形成JSON.stringify(data)的方式:

"{"messengerState":{"IsOpen":true,"ConversationStates":[{"PartnerId":"64c71990-9ddc-4967-8821-a8e5936560a3","IsEnabled":true},{"PartnerId":"64c71990-9ddc-4967-8821-a8e5936560a3","IsEnabled":true}]}}"

这是$("#messenger-set-state-url").val()

的价值
"/Messenger/Messenger/SetMessengerState"

控制器方法:

[HttpPost]
public async Task<ActionResult> SetMessengerState(MessengerStateInfo messengerState)
{
    var user = User.ApplicationUser();

    if (user == null)
        return null;

    bool success = await MvcApplication.Messenger.SetState(user, messengerState) != null;

    return Json(success, JsonRequestBehavior.DenyGet);
}

最后,这是MessengerStateInfoConversationStateInfo

public class MessengerStateInfo
{
    public bool IsOpen { get; set; }
    public ICollection<ConversationStateInfo> ConversationStates { get; set; }

    public MessengerStateInfo()
    {
        ConversationStates = new ConversationStateInfo[0];
    }
}

public class ConversationStateInfo
{
    public string PartnerId { get; set; }
    public bool IsEnabled { get; set; }
}

我无法找到我做错的地方。帖子永远不会到达控制器方法。我之前尝试过使用基本(string)参数,它工作得很好,但它根本无法解决复杂的对象。

3 个答案:

答案 0 :(得分:1)

感谢您撰写一个可以重现问题的详细问题。

model binder期望您的ICollection可写,但数组不能以这种方式写入。做一个简单的实验:

ICollection<int> a = new int[0];
a.Clear();

A&#34;收集是只读的#34;异常将被抛出。

现在,你如何解决这个问题。将您的MessengerStateInfo班级定义更改为以下内容:

public class MessengerStateInfo
{
    public bool IsOpen { get; set; }
    public ICollection<ConversationStateInfo> ConversationStates { get; set; }
}

这里我们删除了构造函数,它允许模型绑定器创建List<>类型的新实例。这一个当然是读写的,绑定成功。

以下是来自模型绑定器源代码的相关代码片段。这个来自System.Web.ModelBinding.CollectionModelBinder<TElement> class:

protected virtual bool CreateOrReplaceCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, IList<TElement> newCollection)
{
    CollectionModelBinderUtil.CreateOrReplaceCollection<TElement>(bindingContext, newCollection, () => new List<TElement>());
    return true;
}

这一个来自System.Web.ModelBinding.CollectionModelBinderUtil

public static void CreateOrReplaceCollection<TElement>(ModelBindingContext bindingContext, IEnumerable<TElement> incomingElements, Func<ICollection<TElement>> creator)
{
    ICollection<TElement> model = bindingContext.Model as ICollection<TElement>;
    if ((model == null) || model.IsReadOnly)
    {
        model = creator();
        bindingContext.Model = model;
    }
    model.Clear();
    foreach (TElement local in incomingElements)
    {
        model.Add(local);
    }
}

正如您可以从此代码中清楚地看到,如果在其上调用了集合非空Clear方法,则在您的情况下会导致异常。另一方面,如果集合为空,则执行new List<>,这将生成一个全新的(可写)对象。

注意:实施细节可能因软件版本而异。上面的代码可能与您使用的库版本中的实际代码不同。但原则仍然是相同的。

这里有一个提示如何比向Stackoverflow输入问题更快地找到原因。

public async Task<ActionResult> SetMessengerState(MessengerStateInfo messengerState)上放置断点并观察断点未被击中。打开chrome控制台并获取错误图标的概念。

enter image description here

单击图标以查看底部的错误消息。 现在点击错误消息中的链接,您将看到此屏幕:

enter image description here

最后点击&#34; Name&#34;中的请求。柱。你会看到这个:

enter image description here

这将为您提供堆栈跟踪的实际错误消息。在大多数情况下,您将能够分辨出此消息中的错误。在这种特殊情况下,您将立即看到一个阵列被尝试清除并失败。

答案 1 :(得分:0)

该错误实际上是在修改ICollection<ConversationStateInfo>

中的ConversationStates = new ConversationStateInfo[0];对象

将其更改为列表可以解决问题。

答案 2 :(得分:0)

将$ .ajaxSettings.traditional设置为true。我已经遇到了同样的问题,并且适合我

$.ajaxSettings.traditional = true;

 $('#btn').click(function () {
     var array = [];
     var url = '/Controller/Action';
     $.post(url, { array : array });
   });
 });