2013-05-30 11 views
22

Mam ten linkAsp.net WebAPI deserializes ciąg czas UTC na czas lokalny

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

i to WebAPI Endpoint

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

Problem jest taki, że w obliczu startDate otrzymał rozszeregować ciąg jako czas lokalny, "11/5/1994 9:15:30 AM" zamiast pobytu w czasie UTC, co chciałem "11/5/1994 5:15:30 PM".

Używam VS2012 update2, najnowszego pakietu Json.net nuget. Jeśli jednak używam json.net w osobnej aplikacji konsolowej do testowania, ten sam ciąg "1994-11-05T17: 15: 30Z" jest w stanie poprawnie przekształcić się w deserializację na "11/5/1994 5:15:30 PM ".

Ktoś wie, co tu jest nie tak?

+0

Nie możesz uzyskać czasu UTC od deserializowanej daty? –

+0

Znaleziono rozwiązanie http://stackoverflow.com/questions/12246254/asp-net-web-api-modelbinders –

Odpowiedz

24

Mimo że masz już found a solution na swoje pytanie, pomyślałem, że postaram się wyjaśnić, dlaczego nie zadziałało zgodnie z oczekiwaniami.

WebApi używa negocjacji typu treści do określenia, którego parser należy użyć do odczytu danych. Oznacza to, że będzie on patrzył na nagłówek żądania, aby dokonać ustalenia. Jeśli nagłówek Content-Type jest ustawiony na application/json, użyje Json.Net do przeanalizowania zawartości i podania jej do swojej metody.

Żądanie HTTP GET, takie jak tutaj, nie ma zestawu typu zawartości. "Zawartość" w tym przypadku jest po prostu ciągiem zapytania z adresu URL. WebApi nie spodziewa się znaleźć tutaj danych JSON, więc nie będzie próbował użyć parsera JSON, aby to zrozumieć. Nawet jeśli tak, ciąg znaków przekazywany do metody GetAll nie jest nawet prawidłowym JSON. (Musiałby być podany jako ważny).

Teraz, jeśli chcesz zmienić metodę akceptowania żądania POST, i ustaw nagłówek typu zawartości na application/json i podaj datę jako ciąg JSON w Ciało, to WebApi użyje Json.Net do parsowania i będzie działać tak, jak oczekujesz.

Na przykład, powiedzmy metoda wyglądał następująco:

[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; 
    } 
} 

I złożył wniosek podobny do poniższego (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" 

Odpowiedź będzie wyglądać następująco:

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"} 

Jak widać, poprawnie rozpoznaje datę UTC w tym scenariuszu.

+0

Naprawdę doceniam twoje poświęcenie czasu na wyjaśnienia. +1 i oznaczono Cię jako odpowiedź. –

+0

Dzięki, cieszę się, że znalazłeś wyjaśnienie pomocne. –

+0

Niestety nie działa to w przypadku żądania GET z atrybutem [FromUri] dołączonym do Twojego zapytania. Nie wiem, jak to działa. – SamSerious

3

Jeśli chcesz zmodyfikować sposób Asp WebAPI analizuje uri parametry swoich żądań GET, można napisać niestandardowy IModelBinder takiego:

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; 
    } 
} 

A następnie zastąpić domyślny Spoiwo:

GlobalConfiguration.Configure(configuration => configuration.BindParameter(typeof(DateTime), new UtcDateTimeModelBinder());); 

Parameter Binding in ASP.NET Web API

+0

Jeśli Twoja data jest już sformatowana w formacie ISO "1994-11-05T17: 15: 30Z". następnie DateTime.TryParse należy zmodyfikować jako DateTime.TryParse (stringValue, null, DateTimeStyles.RoundtripKind, out parsedDate) –

Powiązane problemy