Подтвердить что ты не робот

Asp.net WebApi десериализует строку времени UTC по местному времени

У меня есть этот URL

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

и эта конечная точка вебапи

[ActionName("GetAll")]
public object GetAll(DateTime startDate)
{
     ...
}

Проблема, с которой я сталкиваюсь, состоит в том, что startDate получил десериализованную строку как местное время, " 5.11.1994 9:15:30 AM " вместо того, чтобы оставаться во времени UTC, которое я хотел " 5.11.15: 5:15: 30 вечера ".

Я использую VS2012 update2, последний пакет Jget.net nuget. Однако, если я использую json.net в отдельном консольном приложении для тестирования, та же самая строка " 1994-11-05T17: 15: 30Z " способна правильно десериализоваться в " 05.11.1994 17:15:30 ".

Кто-нибудь знает, что здесь не так?

4b9b3361

Ответ 1

Хотя вы уже нашли решение для своего вопроса, я подумал, что сделаю попытку объяснить, почему он не работает так, как вы ожидали.

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 в этом сценарии.

Ответ 2

Если вы хотите изменить способ, которым Asp WebApi анализирует параметры uri ваших запросов GET, вы можете написать собственный 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()););

Связывание параметров в ASP.NET Web API