Asp.net WebApi将UTC时间字符串反序列化为本地时间

Ray*_*Ray 27 utc json.net asp.net-web-api

我有这个网址

http://example.com/api/record/getall?startdate=1994-11-05T17:15:30Z

和这个webapi端点

[ActionName("GetAll")]
public object GetAll(DateTime startDate)
{
     ...
}
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是startDate收到反序列化的字符串作为本地时间," 11/5/1994 9:15:30 AM "而不是停留在UTC时间,这就是我想要的" 11/5/1994 5:15:下午30点 ".

我正在使用VS2012 update2,最新的Json.net nuget包.但是,如果我在一个单独的控制台应用程序中使用json.net进行测试,则相同的字符串" 1994-11-05T17:15:30Z "能够正确地反序列化为" 11/5/1994 5:15:30 PM ".

谁知道这里有什么问题?

Bri*_*ers 31

虽然你已经为你的问题找到了解决方案,但我想我会解释为什么它没有像你预期的那样工作.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

你提出了这样的请求(请注意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"
Run Code Online (Sandbox Code Playgroud)

响应如下:

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"}
Run Code Online (Sandbox Code Playgroud)

如您所见,它确实在这种情况下正确识别了UTC的日期.

  • 是的,虽然这解释了为什么会发生这种情况,但我想知道如何解决这个问题而不需要在控制器内部进行手动日期转换. (4认同)

Ato*_*osk 5

如果要修改 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后覆盖默认绑定器:

GlobalConfiguration.Configure(configuration => configuration.BindParameter(typeof(DateTime), new UtcDateTimeModelBinder()););
Run Code Online (Sandbox Code Playgroud)

ASP.NET Web API 中的参数绑定