Cutting Edge

Criando exibições otimizadas para mobilidade no ASP.NET MVC 4, Parte 2: Usando o WURFL

Dino Esposito

Dino EspositoLembranças antigas das guerras de navegadores de uma década atrás ainda assustam os desenvolvedores hoje em dia. Um dos motivos do sucesso universal do jQuery pode ser sua capacidade de esconder diferenças sutis e menos sutis nas implementações do DOM (Document Object Model) e do JavaScript em navegadores. Hoje, os desenvolvedores supostamente podem se concentrar nos recursos e esquecer dos recursos específicos de navegador. Verdade? Isso é válido para navegadores de desktop, mas bem menos para navegadores móveis.

Os navegadores móveis ocupavam um espaço que é muito mais fragmentado do que os navegadores de desktop ocupavam uma década atrás. A quantidade de diferentes navegadores de desktop a serem considerados pode chegar a dezenas; a quantidade de diferentes navegadores móveis tem uma ordem de magnitude de milhares. Provavelmente o formato dos evoluirão em ambas as direções — ficando menores como os smartphones e minitablets, mas também maiores como com as smart TVs. O segredo do sucesso de hoje e de amanhã é proporcionar aos usuários de cada classe de dispositivos a experiência adequada. Para isso, você precisa detectar o dispositivo primeiro e depois suas capacidades.

O bom e velho banco de dados de recursos do navegador

Desde a versão 1.0, o ASP.NET oferece um banco de dados de recursos do navegador. A propriedade Browser exposta pelo objeto HttpRequest pode ser consultada para um pequeno conjunto de recursos do navegador solicitante. A mecânica da infraestrutura de recursos do navegador é bastante simples e eficaz. Ao criar o objeto HttpRequest, o tempo de execução do ASP.NET capta a cadeia de caracteres do agente do usuário e a executa no banco de dados do navegador ASP.NET. Usando a cadeia de caracteres do agente do usuário como chave, ele recupera uma lista de recursos conhecidos e armazena os valores em um objeto HttpBrowserCapabilities que os desenvolvedores acessam via Request.Browser. Até onde posso ver, esse recurso é exclusivo do ASP.NET, e nenhuma outra plataforma de desenvolvimento Web tem algo semelhante.

Quando você verificar os fundamentos dos recursos do navegador, ficará claro que essa estrutura sofre de uma deficiência relevante: para ser eficaz, o banco de dados que armazena os recursos do navegador deve ser atualizado constantemente como novos dispositivos e versões do navegador lançados no mercado.

Originalmente, a Microsoft desenvolveu a estrutura de recursos do navegador como auxílio para se distinguir entre alguns navegadores de desktop. O advento dos dispositivos móveis mudou completamente a magnitude do problema. Por um tempo, a Microsoft apoiou o projeto MDBF (Mobile Device Browser File) (mdbf.codeplex.com), que visa criar um banco de dados avançado contendo definições de recursos de navegadores e dispositivos móveis individuais. A ideia era que, simplesmente substituindo os arquivos padrão .browser obtidos no ASP.NET com o banco de dados MDBF, você poderia acessar informações detalhadas sobre o navegador e o dispositivo móvel via Request.Browser. Dois anos atrás, no entanto, o projeto foi descontinuado. Mas a ideia subjacente continua bastante válida e provavelmente é a maneira mais eficaz de fornecer conteúdo personalizado e altamente otimizado para algumas classes de dispositivos. Vou explorar como fazer isso usando o ASP.NET MVC 4. No próximo exemplo, qualquer informação sobre dispositivos é fornecida pelo WURFL (Wireless Universal Resource FiLe) em wurfl.sourceforge.net. Entre outras coisas, o WURFL é o repositório de descrição de dispositivos (DDR) usado por muitas empresas de grande porte, incluindo Google e Facebook. Você pode ler mais sobre WURFL e DDRs na minha série anterior de colunas sobre desenvolvimento de sites para celular, começando com “Desenvolvimento de site para celular: marcação” (msdn.microsoft.com/magazine/jj133814).

A face móvel do ASP.NET MVC 4

No ASP.NET MVC 4, invocando o código na Figura 1 dentro do global.asax, você prepara o terreno para ter até três representações diferentes da mesma exibição do Razor.

Figura 1 Instruindo o ASP.NET MVC 4 a oferecer suporte para até três representações da mesma exibição do Razor

public class DisplayConfig
{
  public static void RegisterDisplayModes(
    IList<IDisplayMode> displayModes)
  {
    var modeSmartphone = new DefaultDisplayMode("smart")
    {
      ContextCondition = (c => c.Request.IsSmartphone())
   };
   var modeTablet = new DefaultDisplayMode("tablet")
   {
     ContextCondition = (c => c.Request.IsTablet())
   };
   var modeDesktop = new DefaultDisplayMode("")
   {
     ContextCondition = (c => c.Request.IsDesktop())
   };
   displayModes.Clear();
   displayModes.Add(modeSmartphone);
   displayModes.Add(modeTablet);
   displayModes.Add(modeDesktop);
  }
}

RegisterDisplayModes instrui o tempo de execução do ASP.NET MVC 4 a considerar três representações diferentes para cada exibição. Isto significa que, cada vez que o controlador invocar uma exibição, o nome real da exibição (digamos, "índice") será examinado pelo provedor do modo de exibição e será alterado para index.smart ou index.tablet se a condição de contexto definida para smartphones ou tablets for verificada. A ordem em que os objetos de modo de exibição são inseridos no provedor é fundamental. A pesquisa, na verdade, para na primeira ocorrência. Suponhamos, por exemplo, que a solicitação HTTP resulte no seguinte código:

public ActionResult Index(){  return View();}

Enquanto resolve o nome de exibição, o ASP.NET MVC 4 confere a coleção de modos de exibição e verifica primeiro o modo de exibição para smartphone. A condição de contexto para smartphones retorna true ou false dependendo da implementação do IsSmartphone. Se true, uma exibição com o sufixo "smart" é selecionada, se existente. Caso contrário, a pesquisa continua com os próximos modos de exibição disponíveis.

Definir o número de modos de exibição depende de você, assim como decidir as condições que determinam quais solicitações pertencem a quais modos de exibição.

Para mapear uma solicitação para um modo de exibição, você precisa detectar os dispositivos. No entanto, você não terá uma exibição para cada dispositivo possível, mas sim uma exibição para cada classe de dispositivos para qual você pretende oferecer suporte em seu aplicativo.

Tornando a detecção de dispositivos inteligente

No final das contas, a detecção de dispositivos envolve "xeretar" a cadeia de caracteres do agente do usuário. As cadeias de caracteres do agente do usuário não são uma ciência exata e podem exigir bastante trabalho de análise e normalização para que sejam digeridas e facilmente mapeadas para uma lista de recursos. Manter esse banco de dados custa caro, pois você deve verificar cada nova versão do navegador e lançamento de dispositivo e a personalização do sistema operacional feita por OEMs. Além disso, para cada dispositivo único identificado, você deve descobrir os recursos e armazená-los eficientemente em um banco de dados.

Algumas empresas atual nesse setor e vendem seus produtos de acordo com vários modelos, incluindo soluções baseadas em nuvem razoavelmente baratas. A estrutura mais popular para a detecção de dispositivos é o WURFL (mencionado anteriormente) — uma biblioteca de plataforma cruzada disponível para uma variedade de linguagens que também são de código-fonte aberto, de acordo com a AGPL (Affero General Public License). Para os desenvolvedores do ASP.NET MVC 4, o WURFL também está disponível como um pacote NuGet. No espaço do Microsoft .NET Framework, outra opção é fornecida pelo banco de dados 51Degrees (51degrees.mobi). Na próxima seção, examinarei como uma estrutura de detecção de dispositivos pode ser usada para direcionar modos de exibição em um aplicativo ASP.NET MVC 4.

Adicionando WURFL a modos de exibição

O código na Figura 1 é essencialmente centrado no delegado Context­Condition:

Boolean ContextCondition(HttpContextBase)

A assinatura é autoexplicativa: passa o contexto HTTP e espera uma resposta booliana. A lógica no delegado deve consumir qualquer informação no contexto HTTP e determinar se a solicitação pode ser atendida uma exibição como pretendido pelo modo de exibição. Como exemplo, vou começar de onde parei na minha última coluna, “Criando exibições otimizadas para mobilidade no ASP.NET MVC 4” (msdn.microsoft.com/magazine/dn296507). O método IsTablet usado na Figura 1 é um método de extensão adicionado à classe HttpRequestBase. Este é o código:

public static class HttpRequestBaseExtensions
{
  public static Boolean IsTablet(this HttpRequestBase request)
  {
    var ua = userAgent.ToLower();
    return ua.Contains("ipad") || ua.Contains("gt-");
  }
}

Com a evolução rápida dos dispositivos móveis, está mais difícil encontrar e manter uma definição estática do que é um tablet ou smartphone. Ser um tablet ou smartphone mal pode ser considerado como uma característica física, como se o dispositivo oferece suporte à transmissão de vídeo em Flash (.flv) ou imagens embutidas. A classe de dispositivo é mais um recurso virtual cuja definição depende inteiramente da equipe de desenvolvimento. Recursos virtuais normalmente são implementados como a combinação lógica de vários recursos físicos.

Para adicionar WURFL ao seu código ASP.NET MVC 4, basta invocar NuGet e obter a API oficial do WURFL. O pacote instala o banco de dados WURFL, um arquivo zipado, na pasta App_Data. O banco de dados é um instantâneo relativamente recente das informações dos dispositivos; para obter atualizações semanalmente, você precisa comprar uma licença comercial do ScientiaMobile (scientiamobile.com).

Uma vez implantado o WURFL, você adiciona a seguinte linha a Application_Start:

WURFLManagerBuilder.Build(new ApplicationConfigurer());

Isso irá carregar o banco de dados na memória e garantirá que todos os dados sejam armazenados em cache para o acesso mais rápido possível. Para executar uma consulta WURFL, você precisa do seguinte código que deve ser executado em cada solicitação para uma exibição em HTML:

var userAgent = ...; // Typically read from Request object
var device = WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);

Novamente, o código é autodescritivo. A estrutura WURFL obtém o agente do usuário e retorna (em questão de milissegundos) todas as informações que sabe sobre o dispositivo. A informação é expressa na forma de uma coleção de nome/valor, onde o nome do recurso e seu valor retornado são cadeias de caracteres. Transformar cadeias de caracteres em dados fortemente tipados (por exemplo, inteiros ou boolianos) é sua responsabilidade. O WURFL oferece mais de 500 recursos por dispositivo catalogados em diversas categorias. Claramente, você não está interessado em todos eles. Eu diria que um número mais realista é um décimo disso, que também é condizente com o número de recursos suportados pelo projeto MDBF já obsoleto. De qualquer forma, esse número equivale a muito mais recursos do que você pode verificar com consultas de mídia CSS3. As consultas de mídia têm cinco propriedades no total, apenas um ou dois delas (largura e orientação) são usadas frequentemente. Veja aqui como verificar confiavelmente para tablets usando o WURFL:

public static class HttpRequestBaseExtensions
{
  public static Boolean IsTablet(this HttpRequestBase request)
  {
    var device =
       WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
   return device.IsTablet();
  }
}

IsTablet é um método de extensão para o tipo IDevice do WURFL que acabei de criar para superar a natureza de tipo fraco do WURFL:

public static Boolean IsTablet(this IDevice device){
  return device.GetCapability("is_tablet").ToBool();
}

Observe que ToBool é ainda outro método de extensão que envolve apenas uma chamada para Boolean.TryParse.

A utilidade dos métodos de extensão para manter o código limpo e elegante é ainda mais evidente no código de exemplo que detecta smartphones, mostrado na Figura 2. Não há provavelmente nenhuma definição comum de smartphone que seja aceitável para todos. Através da combinação de várias propriedades do WURFL, você pode criar sua própria definição de smartphone, como mostrado na Figura 2.

Figura 2 Definição de um smartphone como recurso virtual

public static class HttpRequestBaseExtensions
{
  public static Boolean IsSmartphone(this HttpRequestBase request)
  {
    var device =
      WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
    return device.IsWireless() && !device.IsTablet() &&
      device.IsTouch() &&
      device.Width() > 320 &&
      (device.HasOs("android", new Version(2, 2)) ||
      device.HasOs("iphone os", new Version(3, 2)) ||
      device.HasOs("windows phone os", new Version(7, 1)) ||
      device.HasOs("rim os", new Version(6, 0)));
  }
}

O código na Figura 2 define um smartphone como um dispositivo sem fio que não é um tablet, é habilitado para toque, tem pelo menos 320 pixels de largura e executa qualquer um dos sistemas operacionais especificados. Todos os métodos usados na variável de dispositivo são métodos de extensão criados com base nos recursos nativos do WURFL.

Detecção por servidor e design responsivo por cliente

O foco em alguns segmentos do setor na detecção de recursos por cliente é justificado pelo objetivo de aproveitar os recursos avançados de HTML5 e CSS3 em sites. No entanto, isso não tem muito a ver com a criação de sites para celulares. Detecção de recursos por cliente é limitada a o que o navegador que permite detectar — no melhor das hipóteses, as cinco propriedades às quais você tem acesso por meio de consultas de mídia e tudo o que pode ser testado por meio de programação. Para distinguir um dispositivo Android de um iPhone, você ainda precisa "xeretar" o agente do usuário — sem contar que executar o Android 2.2, por exemplo, não diz muito sobre os recursos reais do dispositivo.

Se você realmente precisa fornecer marcação ad hoc para dispositivos (pequenos e grandes), a detecção de dispositivos e recursos por servidor é a única solução. O WURFL torna isso rápido e fácil. 

A detecção por servidor e o design responsivo por cliente não são uma escolha entre um ou outro. A detecção por servidor envolve apenas a identificação do dispositivo e o modo de exibição relacionado. A marcação que você fornece pode conter facilmente consultas de mídia, layouts fluidos e qualquer outra coisa que ajude a melhorar o resultado. Se você gosta de acrônimos, isso é o que chamamos de RESS (que se refere a Design responsivo + Componentes do servidor). Detecção por servidor apenas agrega um nível a mais de abstração ao processo de criação de exibições personalizadas.

Dino Esposito é o autor de “Architecting Mobile Solutions for the Enterprise” (Microsoft Press, 2012) e de “Programming ASP.NET MVC 5”, que será lançado em breve pela Microsoft Press. Instrutor técnico das plataformas .NET e Android no JetBrains, e palestrante frequente em eventos do setor no mundo todo, Esposito compartilha sua visão de software em software2cents.wordpress.com e no Twitter, em Twitter.com/despos.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Mani Subramanian (Microsoft)