WebAPI e retorno de campo DateTime - Request Ajax trazendo valor em formato texto ao invés de objeto tipo DateTime


Detalhando a Situação

Recentemente passei por um problema de programação que já havia passado antes, mas contornado "via código".

O problema não é incomum e depende exclusivamente de como você precisa tratar tal tipo de conteúdo em sua View.
Considere o seguinte fluxo (sem entrar em muitos detalhes): 
  1. View faz o request Ajax a um Método no Controller WebAPI.
  2. Método processa e retorna um registro ou lista do Domínio.
  3. Método converte dados do Domínio para DTO.
  4. Método retorna os dados para a View.
Uma classe DTO é uma simplificação da classe de Domínio e é usada como container para serialização no retorno dos valores/registros lidos do banco de dados para a View que fez a requisição.


O Problema

O método da WebAPI retorna para a View usando o método Request.CreateResponse().

No meu caso utilizo ExtJS como framework como client/browser, então os requests avulsos da view são feitos com a função Ext.Ajax.request(). Não testei usando jQuery, mas creio que não haja diferença.

Quando um request é feito as propriedades do tipo DateTime retornam o valor como texto no seguinte formato: 2014-01-01T00:00:00. Só que para ser trabalhando na view de maneira direta, ou seja, cálculos, componentes que só recebem valor data/hora (ou data), é preciso que esse texto seja convertido para um objeto de data Javascript, algo como Date {Mon Jan 01 2014 00:00:00 GMT-0200}.

Considerando um request feito por uma Store do Ext.Net (abstração do ExtJS), se apontamos para o mesmo método no controller da WebAPI, e configurado o Record Field da Store com algo assim: 

<ext:RecordField Name="PropriedadeDataNoDto" Type="Date" DateFormat="yyyy-MM-ddTh:i:s" />

Desta maneira a view consegue fazer com que o retorno seja o esperado, um objeto data/hora.


A Solução

A solução definitiva que encontrei, a que implica que todas as requisições no nível de WebAPI fiquem corretas, está relacionada a configurar um "JsonFormatter" aplicado na serialização do conteúdo, mas não encontrei nenhuma solução para a minha necessidade, nem mesmo os "formatters" padrões tipo ISO e outros, então tive que adequar uma.

Criei uma classe formatter chamada JavaScriptDateTimeConverter (atenção que existe uma classe com mesmo nome).

public class JavaScriptDateTimeConverter : DateTimeConverterBase
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value is DateTime)
        {
            return DateTime.Parse(reader.Value.ToString());
        }
        else
        {
            return reader.Value;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
        }
        else if (value is DateTime)
        {
            DateTime dateTime = (DateTime)value;

            // retornará a data no padrão: new Date(year, month, day, hours, minutes, seconds, milliseconds);
            writer.WriteStartConstructor("Date");
            writer.WriteValue(dateTime.Year);
            writer.WriteValue(dateTime.Month - 1);
            writer.WriteValue(dateTime.Day);
            writer.WriteValue(dateTime.Hour);
            writer.WriteValue(dateTime.Minute);
            writer.WriteValue(dateTime.Second);
            writer.WriteEndConstructor();
        }
        else
        {
            writer.WriteValue(value.ToString());
        }
    }
}

E em WebApiConfig.cs a lógica para que a classe seja utilizada.

public static class WebApiConfig    {
  public static void Register(HttpConfiguration config)
  {
    ...
    config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new SuaNamespace.JavaScriptDateTimeConverter());
    ...
  }
}

Um ponto que não avaliei, pode ser que fazendo um request com algum parâmetro a mais o retorno de propriedades/campos DateTime ocorra como o esperado, mas infelizmente não pude testar, de qualquer não encontrei em nenhum artigo referência a essa possibilidade.

Comentários

Postagens mais visitadas deste blog

Selenium + Firefox = The type initializer for 'System.IO.Compression.ZipStorer' threw an exception

Transmissor sem fio bluetooth Tomate MTB-803 e manual

Problema de rolagem de HTML em iframe no iOS