【问题标题】:Cannot post complex object无法发布复杂对象
【发布时间】:2016-10-26 09:06:53
【问题描述】:

我有这个功能在我看来:

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

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

"{"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) 参数,它工作得很好,但它根本无法处理复杂的对象。

【问题讨论】:

  • 这一行data : JSON.stringify(data),应该是data : data,
  • 我觉得应该是data =g_messengerState
  • 这些都不起作用。
  • public async Task&lt;ActionResult&gt; SetMessengerState([FromBody]MessengerStateInfo messengerState)中使用[FromBody]
  • 我的意思是代替var data = {messengerState: g_messengerState}。应该只是var data = g_messengerState

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


【解决方案1】:

感谢您撰写详细的问题,以便重现问题。

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

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

将抛出“集合为只读”异常。

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

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

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

这里是来自模型绑定器源代码的相关代码sn-ps。这个来自System.Web.ModelBinding.CollectionModelBinder&lt;TElement&gt;类:

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&lt;&gt;,从而生成一个全新的(可写)对象。

注意:实施细节可能因软件版本而异。上面的代码可能与您使用的库版本中的实际代码不同。原理还是一样的。

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

public async Task&lt;ActionResult&gt; SetMessengerState(MessengerStateInfo messengerState)上放一个断点,观察断点没有命中。打开 chrome 控制台并注意错误图标。

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

最后点击“名称”列中的请求。你会看到这个:

这将为您提供堆栈跟踪的实际错误消息。在大多数情况下,您将能够从该消息中看出问题所在。在这种特殊情况下,您将立即看到尝试清除数组并失败。

【讨论】:

  • 非常完整的答案。坚持下去。
【解决方案2】:

错误实际上是修改ConversationStates = new ConversationStateInfo[0];中的ICollection&lt;ConversationStateInfo&gt;对象

将其更改为 List 应该可以解决问题。

【讨论】:

  • 实际上初始化它是问题所在。您可以使用ICollectionIEnumerable 或普通数组,只要您不初始化它就可以正常工作。
  • @AlexandreSeverino 是的。 ConversationStates = new ConversationStateInfo[0]; 基本上说它是一个零元素集合,所以它不会再占用你的帖子数据了。如果您真的想始终初始化此成员,请尝试使用 list 之类的数据结构。
【解决方案3】:

将 $.ajaxSettings.traditional 设置为 true。我已经有同样的问题,并且为我工作

$.ajaxSettings.traditional = true;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 2018-02-20
    • 1970-01-01
    相关资源
    最近更新 更多