Datetime MVC4单页应用程序和日期时间
在使用MVC4新的单页应用程序工具时,我注意到我发现的示例中没有一个包含通过WebApi更新回日期时间的示例。我很快发现了原因 我首先从提供的模板生成标准SPA。然后我打开TodoItem.cs并添加了一个DateTime字段。然后,我按照注释的指示生成控制器。(没有datetime字段,一切正常) 生成所有内容后,我启动应用程序并导航到控制器索引(我称控制器为“任务”)。我得到了0条记录的网格页面,点击了添加按钮。我按预期进入了编辑页面,并在我闪亮的新datetime字段中输入了一些数据,包括日期。然后单击保存 产生了一个错误,表示: 服务器错误:HTTP状态代码:500,消息:反序列化System.Web.HTTP.Data.ChangeSetEntry[]类型的对象时出错。DateTime内容“01/01/2012”不是JSON所要求的以“/Date(”开头,以“/”结尾 该工具似乎还不支持DateTime。我相信我可以经历,花一点时间去弄清楚它,让它发挥作用,但我想我可能会在这里找到一点运气,有人已经解决了这个问题,可以提供见解 有人已经为此奋斗过了吗 更新:我添加了更多的信息,因为我问了这个问题。我尝试使用JSON.Net作为我的格式化程序,如下所示。我认为这将是最终的解决方案,然而,仅仅按照下面的海报建议做是不够的 使用JSON.Net序列化程序时,出现以下错误: 此DataController不支持实体“JObject”的操作“Update” 原因是JSON.Net没有完全填充格式化程序试图反序列化到的对象(System.Web.Http.Data.ChangeSet) 发送的json是:Datetime MVC4单页应用程序和日期时间,datetime,asp.net-mvc-4,asp.net-web-api,asp.net-spa,Datetime,Asp.net Mvc 4,Asp.net Web Api,Asp.net Spa,在使用MVC4新的单页应用程序工具时,我注意到我发现的示例中没有一个包含通过WebApi更新回日期时间的示例。我很快发现了原因 我首先从提供的模板生成标准SPA。然后我打开TodoItem.cs并添加了一个DateTime字段。然后,我按照注释的指示生成控制器。(没有datetime字段,一切正常) 生成所有内容后,我启动应用程序并导航到控制器索引(我称控制器为“任务”)。我得到了0条记录的网格页面,点击了添加按钮。我按预期进入了编辑页面,并在我闪亮的新datetime字段中输入了一些数据,包括
[{"Id":"0",
"Operation":2,
"Entity":
{"__type":"TodoItem:#SPADateProblem.Models",
"CreatedDate":"/Date(1325397600000-0600)/",
"IsDone":false,
"Title":"Blah",
"TodoItemId":1},
"OriginalEntity":
{"__type":"TodoItem:#SPADateProblem.Models",
"CreatedDate":"/Date(1325397600000-0600)/",
"IsDone":false,
"Title":"Blah",
"TodoItemId":1}
}]
内置的Json格式化程序能够将此Json重新构造为一个变更集对象,该变更集对象在Entity和OriginalEntity字段中嵌入了TodoItem对象
有人让JSON.Net正确地反序列化了吗?问题是,在当前的测试版中,ASP.Net Web API使用了
DataContractJsonSerializer
,它在DateTime
的序列化方面存在众所周知的问题。最近,微软Connect因该问题引发了一个安静的bug;MS回应说,他们已经有一个跟踪问题的bug,但它不会在.Net 4.5/VS11时间范围内修复
幸运的是,您可以替换另一个JSON序列化程序,例如优秀的
ASP.NET团队的Henrik Nielsen发布了一篇文章,展示了如何将JSON.NET与ASP.NET Web API结合使用。下面是他使用JSON.Net实现的MediaTypeFormatter
(它还需要连接到ASP.NETWebAPI配置,Henrik的博客也演示了这一点)
公共类JsonNetFormatter:MediaTypeFormatter
{
私有只读JsonSerializerSettings设置;
公共JsonNetFormatter(JsonSerializerSettings=null)
{
this.settings=设置??新建JsonSerializerSettings();
添加(新的MediaTypeHeaderValue(“应用程序/json”);
编码=新的UTF8编码(假、真);
}
受保护的覆盖布尔CanReadType(类型)
{
返回类型!=typeof(IKeyValueModel);
}
受保护的覆盖布尔CANRITETYPE(类型)
{
返回true;
}
受保护的重写任务OnReadFromStreamAsync(类型类型、流、HttpContentHeaders contentHeaders、FormatterContext FormatterContext)
{
var ser=JsonSerializer.Create(设置);
返回Task.Factory.StartNew(()=>{
使用(var strdr=newstreamreader(stream))
使用(var jtr=new JsonTextReader(strdr))
{
var deserialized=ser.Deserialize(jtr,类型);
返回反序列化;
}
});
}
受保护的重写任务OnWriteToStreamAsync(类型类型、对象值、流、HttpContentHeaders contentHeaders、FormatterContext FormatterContext、TransportContext TransportContext)
{
JsonSerializer ser=JsonSerializer.Create(设置);
返回Task.Factory.StartNew(()=>
{
使用(JsonTextWriter w=newjsontextwriter(newstreamwriter(流,编码)){CloseOutput=false})
{
序列序列化(w,值);
w、 冲洗();
}
});
}
}
JSON.NET需要$type,而您有u type来指定实体类型,以便它将其转换为JObject
我用下面这句废话说服了他
首先确保JsonSerializerSettings
具有
.typenameholling=Newtonsoft.Json.typenameholling.Objects
然后编写自己的```JsonTextReader
public class MyJsonTextReader : JsonTextReader
{
public MyJsonTextReader(TextReader reader)
: base(reader)
{ }
public override object Value
{
get
{
var o = new ActivityManager.Models.Sched_ProposedActivities();
if (TokenType == JsonToken.PropertyName && base.Value.ToString() == "__type")
return "$type";
if (TokenType == JsonToken.String && Path.ToString().EndsWith(".__type"))
{
string s = base.Value.ToString();
var typeName = Regex.Match(s, ":#.*").ToString().Substring(2) + "." + Regex.Match(s, "^.*:#").ToString().Replace(":#", "");
return
typeName + ", ActivityManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
}
return base.Value;
}
}
}
并使用它通过使用(MyJsonTextReader jsonTextReader=new MyJsonTextReader(streamReader))反序列化Json,我遇到了完全相同的问题。我花了太多时间试图让json.net正常工作。我最终提出了一个解决方案,您可以在示例项目中坚持使用doitemsviewmodel.js:
self.IsDone = ko.observable(data.IsDone);
self.EnterDate = ko.observable(data.EnterDate);
self.DateJson = ko.computed({
read: function () {
if (self.EnterDate() != undefined) {
var DateObj = new Date(parseInt(self.EnterDate().replace("/Date(", "").replace(")/", ""), 10)); //.toDateString();
var ret = DateObj.getMonth() + 1 + "/" + DateObj.getDate() + "/" + DateObj.getFullYear();
return ret;
}
else {
return self.EnterDate();
}
},
write: function (value) {
var formattedDate = "\/Date(" + Date.parse(value) + ")\/"
self.EnterDate(formattedDate);
}
});
upshot.addEntityProperties(self, entityType);
周围的代码行包含在上下文中。我在以下评论中发现了这一点:
您还需要将_Editor.cshtml中的html更改为绑定到“DateJson”,而不是“EnterDate”
这当然是一个难题,但它有工作的优点,这不是一个小壮举。您还可以通过添加以下代码使JQuery日历弹出窗口工作 在示例MVC 4 SPA项目中的TodoItemsViewModel.js底部添加以下内容:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("getDate");
if (value - current !== 0) {
//$(element).datepicker("setDate", value);
$(element).val(value.toString());
}
}
}
ko.bindingHandlers.date = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var jsonDate = "/Date(12567120000-1000)/";
var value = new Date(parseInt(jsonDate.substr(6)));
var ret = value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
element.innerHTML = ret;
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
}
};
这是绑定到日期选择器的_Editor.cshtml代码的外观
<p>
EnterDate:
@*<input name="EnterDate" data-bind="value: EnterDate, autovalidate: true" />
<span class="error" data-bind="text: EnterDate.ValidationError"></span>*@
<input name="DateJson" data-bind="datepicker: DateJson, datepickerOptions: { minDate: new Date() }" />
<span class="error" data-bind="text: DateJson.ValidationError"></span>
</p>
输入日期:
@*
*@
<p>
EnterDate:
@*<input name="EnterDate" data-bind="value: EnterDate, autovalidate: true" />
<span class="error" data-bind="text: EnterDate.ValidationError"></span>*@
<input name="DateJson" data-bind="datepicker: DateJson, datepickerOptions: { minDate: new Date() }" />
<span class="error" data-bind="text: DateJson.ValidationError"></span>
</p>