我有这个网址
http://example.com/api/record/getall?startdate=1994-11-05T17:15:30Z
和此webapi端点
[ActionName("GetAll")]
public object GetAll(DateTime startDate)
{
...
}
我遇到的问题是startDate收到了反序列化的字符串作为本地时间,“ 11/5/1994 9:15:30 AM ”而不是停留在我想要的UTC时间“ 11/5/1994 5:15:30 PM ”。
我正在使用VS2012 update2,最新的Json.net nuget包。但是,如果我在一个单独的控制台应用程序中使用json.net进行测试,则相同的字符串“ 1994-11-05T17:15:30Z ”能够正确地反序列化为“ 11/5 / 1994 5:15:30 PM “。
有人知道这里有什么问题吗?
答案 0 :(得分:28)
虽然您已经found a solution提出了问题,但我想我会解释为什么它不能按预期运作。
WebApi使用内容类型协商来确定在读取数据时要使用的解析器。这意味着它将查看请求的Content-Type
标题以进行确定。如果Content-Type
标头设置为application/json
,那么它将使用Json.Net解析内容并将其提供给您的方法。
HTTP GET请求(例如您在此处创建的请求)没有设置内容类型。在这种情况下,“内容”实际上只是来自URL的查询字符串。 WebApi不希望在这里找到JSON数据,因此它不会尝试使用JSON解析器来理解它。即使它确实如此,您传递给GetAll方法的字符串甚至不是有效的JSON。 (需要引用它才有效。)
现在,如果您要更改方法以接受POST请求,并将内容类型标头设置为application/json
并将日期作为JSON字符串传递到正文中,那么WebApi将使用Json.Net解析它,它会像你期望的那样工作。
例如,假设您的方法如下所示:
[HttpPost]
public object GetAll([FromBody]DateTime startDate)
{
try
{
return new
{
StartDate = startDate.ToString("yyyy-MM-dd HH:mm:ss"),
StartDateKind = startDate.Kind.ToString(),
};
}
catch (Exception ex)
{
return ex.Message;
}
}
你提出了这样的请求(请注意POST):
POST http://localhost:57524/api/values/GetAll HTTP/1.1
Content-Type: application/json
Content-Length: 22
Host: localhost:57524
"1994-11-05T17:15:30Z"
响应如下:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 31 May 2013 01:25:48 GMT
Content-Length: 57
{"StartDate":"1994-11-05 17:15:30","StartDateKind":"Utc"}
正如您所看到的,它确实在这种情况下正确识别了UTC的日期。
答案 1 :(得分:3)
如果要修改Asp WebApi解析GET请求的uri参数的方式,可以像这样编写自定义IModelBinder
:
public class UtcDateTimeModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var stringValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName)?.RawValue as string;
DateTime parsedDate;
if (!DateTime.TryParse(stringValue, null, DateTimeStyles.AdjustToUniversal, out parsedDate))
{
return false;
}
bindingContext.Model = parsedDate;
return true;
}
}
然后覆盖默认活页夹:
GlobalConfiguration.Configure(configuration => configuration.BindParameter(typeof(DateTime), new UtcDateTimeModelBinder()););