我的观点有这个功能:
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);
}
最后,这是MessengerStateInfo
和ConversationStateInfo
:
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
)参数,它工作得很好,但它根本无法解决复杂的对象。
答案 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控制台并获取错误图标的概念。
单击图标以查看底部的错误消息。 现在点击错误消息中的链接,您将看到此屏幕:
最后点击&#34; Name&#34;中的请求。柱。你会看到这个:
这将为您提供堆栈跟踪的实际错误消息。在大多数情况下,您将能够分辨出此消息中的错误。在这种特殊情况下,您将立即看到一个阵列被尝试清除并失败。
答案 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 });
});
});