Dinâmica e ativa, parte 2: Escrevendo e depurando serviços para blocos dinâmicos

Na parte 1 desta série, exploramos a natureza das atualizações de bloco, atualizações de notificação e notificações do sistema que conferem uma característica "dinâmica" à experiência geral do usuário do Windows 8. Em particular, vimos como essas notificações são compostas de cargas XML que são emitidas localmente, de um aplicativo em execução ou uma tarefa em segundo plano ou podem ser fornecidas sob solicitação de um serviço online.

As ações de gerar uma carga XML e emiti-la em um aplicativo em execução são fáceis de desenvolver e depurar dentro de um aplicativo usando o Visual Studio Ultimate 2012 ou o Visual Studio Express 2012 for Windows 8. Em App tiles and badges sample (Exemplo de blocos e notificações de aplicativo), Secondary tiles sample (Exemplo de blocos secundários) e Scheduled notifications sample (Exemplo de notificações agendadas), você obtém todas as informações de que precisará aqui.

O que exige um pouco mais de esforço é desenvolver e depurar um serviço Web para dar suporte a atualizações periódicas, além de notificações por push. O lado do cliente dessa história é bem demonstrado em Exemplos de notificações periódicas e por push do cliente, mas para aproveitar esse exemplo você precisará trabalhar com alguns serviços! Nesta postagem, veremos especificamente como desenvolver os serviços de dão suporte a notificações periódicas para atualizações de bloco e de notificação, com foco no uso de ferramentas do Visual Studio e no localhost para depurar os serviços antes de implantá-los em um ambiente de produção. Também começaremos a explorar como você pode usar os Serviços Móveis do Windows Azure para essa finalidade, que também são muito úteis para dar suporte a notificações por push, conforme veremos na parte 3.

A natureza básica dos serviços

O que um serviço que dá suporte a atualizações periódicas de bloco e notificação faz exatamente? Vamos começar examinando o que significa "serviço", porque essa palavra muitas vezes intimida os desenvolvedores que trabalham basicamente com aplicativos cliente.

Em poucas palavras, serviço é um código colocado em um servidor Web que é executado nesse servidor sempre que uma solicitação é feita. A páginas HTML (.htm ou .html) não são assim: como não existe nenhum código de servidor, o servidor retorna apenas o texto da página e todo o processamento é feito no cliente (inclusive a execução de um eventual script de cliente contido na página). Entretanto, se houver URIs para páginas que terminam com php, asp, aspx, cgi, cshtml ou qualquer outra extensão do servidor, com certeza trata-se de um "serviço" no sentido mais geral.

O serviço é responsável por receber uma solicitação HTTP de cliente, processar os argumentos incluídos no URI e retornar uma resposta de texto apropriada. Para páginas da Web escritas com tecnologias como PHP ou ASP.NET, a resposta deve estar no formato HTML. Os serviços que implementam as APIs da Web, como aquelas do Facebook e Twitter (e milhares de outras!), normalmente aceitam vários parâmetros em cadeias de caracteres de consulta de URI (ou no cabeçalho da solicitação) e retornam dados XML ou JSON em resposta. E observe que estamos falando de serviços criados em transferência de estado representacional, ou REST, em vez daqueles criados em outros protocolos como SOAP, já que REST é um dos serviços mais comuns atualmente.

Dessa forma, o serviço que fornece atualizações periódicas de bloco e/ou notificações é apenas aquele que existe no URI que um aplicativo fornece para o Windows para essa finalidade e que responde às solicitações HTTP com uma carga XML apropriada. Essa carga contém elementos que correspondem a qualquer modelo com suporte, onde imagens são referenciadas com outros URIs (não há suporte para codificação embutida). As informações específicas incluídas na carga também podem vir de qualquer fonte, como veremos a seguir.

Independentemente desses detalhes, porém, esses serviços compartilham uma estrutura semelhante que recebe e processa a solicitação e constrói a resposta XML. Agora veremos como criar essa estrutura básica.

Escrevendo serviços

Para escrever e depurar serviços, você pode usar as ferramentas que desejar contanto que elas deem suporte para a linguagem do servidor escolhida. Aqui, nos concentraremos no Visual Studio, especificamente o Visual Studio Ultimate 2012 e o Visual Studio Express 2012 para Web, sendo que o último está disponível gratuitamente com seu parceiro do Windows que você já conhece. Para instalá-lo, execute o Web Platform Installer através do qual você também pode instalar diversas outras tecnologias relacionadas, como PHP e WebMatrix. Isso também permite que você crie serviços em diversas linguagens.

Como um exemplo bem simples, o seguinte código inclui um serviço PHP completo que responde com uma carga XML de atualização de notificação na qual o valor da notificação é definido como o dia atual do mês (de acordo com o servidor, vejam só!). Ele vem do site de exemplo HelloTiles que está incluído no capítulo 13 do meu livro eletrônico gratuito, Programming Windows 8 Apps with HTML, CSS, and JavaScript (Programando aplicativos do Windows 8 em HTML, CSS e JavaScript):

 <?php
    echo '<?xml version="1.0" encoding="utf-8" ?>';
    echo "<badge value='".date("j")."'/>";
?>

Você pode experimentar esse serviço diretamente. Clique neste link no site Windows Azure que configurei para essa finalidade --https://programmingwin8-js-ch13-hellotiles.azurewebsites.net/dayofmonthservice.php-- e você verá que o XML retornado é parecido com este:

 <?xml version="1.0" encoding="UTF-8"?>
<badge value="24"/>

Experimente o mesmo URI de Exemplos de notificações periódicas e por push do cliente, do cenário 5, “Polling for badge updates” (Sondando atualizações de notificação). Quando você executa o aplicativo pela primeira vez (dentro do Visual Studio Express para Windows), seu bloco aparece na tela inicial como a seguir:

Bloco Polling for badge updates

Agora insira o URI acima na caixa de texto do cenário 5, pressione o botão de atualizações periódicas Iniciar e, supondo que você tenha conectividade de rede, logo verá uma notificação numérica aparecer no bloco do exemplo:

Bloco com notificação numérica

Observe que o Windows tenta sondar uma atualização assim que o aplicativo inicia as atualizações periódicas e depois continua a sondar no intervalo seguinte especificado.

Para obter um exemplo de PHP completo, consulte Criando uma fantástica experiência com o bloco (parte 2), que mostra mais personalizações de uma atualização de bloco. Naquele exemplo, a função hipotética get_trucks_from_database consulta um banco de dados usando um código zip que é incluído nos parâmetros da cadeia de caracteres do URI e cria a resposta XML com os resultados dessa consulta.

O serviço pode fazer muitas coisas desse tipo. Por exemplo:

  • Com o serviço PHP day-of-month acima, o aplicativo pode indicar seu fuso horário local na cadeia de caracteres de consulta para obter uma data precisa, porque o servidor pode facilmente ser localizado em outro fuso horário.
  • Um serviço do tempo pode usar valores de latitude e longitude no URI para recuperar as condições atuais para esse local.
  • O serviço pode gerar imagens na hora, armazená-las em um servidor Web e inserir os URIs apropriados na carga XML.
  • O serviço envia suas próprias solicitações para outros serviços a fim de obter dados adicionais, personalizados com os parâmetros de cadeia de caracteres de consulta (mais detalhes posteriormente).
  • Se um aplicativo habilitar a fila de atualizações de bloco (consulte o método EnableNotificationQueue), ele poderá especificar até cinco URIs separados para sondar atualizações periódicas, conforme demonstrado no cenário 4 de Exemplos de notificações periódicas e por push do cliente. A fila de atualizações de bloco será preenchida com uma atualização de cada URI. Cada um desses URIs, é claro, pode ter sua própria cadeia de caracteres de consulta para permitir uma personalização maior, de forma que o mesmo serviço possa acomodar todas as solicitações.
  • Um aplicativo pode incluir uma id de usuário na cadeia de caracteres de consulta para que o serviço possa consultar em seus armazenamentos de dados o histórico de treinamento dos usuários, as pontuações mais altas em um jogo, novos itens nos feeds nos quais se registraram etc. Nesse caso, uma id de usuário pode ser a PII (Informações de identificação pessoal), para que o aplicativo respeite questões de privacidade. Isso significa que o aplicativo deve criptografar o nome de usuário na cadeia de caracteres de consulta ou usar URIs https://.

Observação: o Windows não fornece um meio dentro do mecanismo de atualização periódica para autenticar o usuário no serviço. Esse nível de suporte só pode ser obtido com notificações por push (ou em cenários empresariais com a funcionalidade Autenticação de Empresa declarada no manifesto).

Obviamente, os serviços podem ser escritos com outras tecnologias. ASP.NET é uma boa escolha porque você pode empregar a mesma biblioteca NotificationsExtensions (escrita em C#) que pode ser usada em um aplicativo para gerar facilmente as cargas XML bem estruturadas.

Para dar um breve exemplo, considere o serviço bem básico WebMatrix que criei para o serviço de exemplo HelloTiles no capítulo 13 do meu livro (consulte o conteúdo complementar). Este serviço em particular retorna apenas uma carga XML fixa (com associações para blocos grandes e quadrados) e é estruturalmente semelhante ao primeiro exemplo ASP.NET fornecido em Criando uma fantástica experiência com o bloco (parte 2):

 @{
  //
  // This is where any other code would be placed to acquire the dynamic content
  // needed for the tile update. In this case we'll just return static XML to show
  // the structure of the service itself.
  // 
  var weekDay = DateTime.Now.DayOfWeek;
}
<?xml version="1.0" encoding="utf-8" ?>
<tile>
    <visual lang="en-US">
        <binding template="TileSquarePeekImageAndText02" branding="none">
            <image id="1" src="https://www.kraigbrockschmidt.com/images/Liam07.png"/>
            <text id="1">Liam--</text>
            <text id="2">Giddy on the day he learned to sit up!</text>
        </binding>
        <binding template="TileWideSmallImageAndText04" branding="none">
            <image id="1" src="https://www.kraigbrockschmidt.com/images/Liam08.png"/>
            <text id="1">This is Liam</text>
            <text id="2">Exploring the great outdoors!</text>
        </binding>
    </visual>
</tile>

Este serviço específico é implantado em https://programmingwin8-js-ch13-hellotiles.azurewebsites.net/Default.cshtml se você quiser experimentá-lo no cenário 4 de Exemplos de notificações periódicas e por push do cliente. Se fizer isso, depois de alguns segundos, você verá as seguintes atualizações de bloco (grandes à esquerda e duas partes do bloco peek quadrado à direita):

liam_1

liam_2liam_tile

Agora vamos escrever a mesma coisa usando a biblioteca NotificationExtensions. A primeira coisa que você precisa fazer é criar uma versão da biblioteca para o seu site, da seguinte forma:

  1. Vá para App tiles and badges sample (Exemplo de blocos e notificações de aplicativo) e copie a pasta NotificationsExtensions desse projeto para uma pasta sua. Também é possível instalar a biblioteca do Visual Studio diretamente clicando com o botão direito do mouse em um projeto, selecionando Manage NuGet Packages… (Gerenciar pacotes NuGet) e pesquisando NotificationsExtensions.WinRT. Entretanto, isso colocaria a biblioteca em um projeto de aplicativo existente, sendo que, aqui, precisamos criar um DLL autônomo.
  2. No Visual Studio Express para Windows, abra o arquivo NotificationsExtensions.csproj.
  3. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto NotificationExtensions, selecione Propriedades e faça as seguintes alterações:
    1. Nas Configurações de Aplicativo, altere Tipo de Saída para Biblioteca de Classes (a .dll). É necessário usar a biblioteca com um site ASP.NET.
    2. Em Configurações de Criação, altere Configuração para Todas Configurações, altere os símbolos de compilação condicionais para NETFX_CORE; WINRT_NOT_PRESENT e verifique se o arquivo de documentação XML está marcado próximo da parte inferior da página. O sinalizador WINRT_NOT_PRESENT permite que a biblioteca compile sem WinRT.
  4. Selecione um Destino de Depuração ou Versão e clique com o botão direito do mouse no projeto Notifications Extensions e selecione Compilar.

Isso gerará um DLL e arquivos associados nessa pasta do projeto. Agora precisamos enviá-lo para um projeto do site efetuando pull.

  1. No Visual Studio Express para Web, clique com o botão direito do mouse no seu projeto do site e selecione Adicionar > a pasta Add ASP.NET > Bin se o site não tiver uma pasta Bin.
  2. Clique com o botão direito do mouse nessa pasta Bin e selecione Adicionar Referência…. Em Adicionar Referência, vá para a pasta bin\Debug ou bin\Release do projeto Notifications Extensions e selecione o DLL que estiver ali.

Se você estiver usando o Visual Studio Ultimate, poderá adicionar o projeto NotificationExtensions à sua solução de site, se desejado, já que a ferramenta pode manipular os dois tipos de projeto. Cuidado para não depurar o código-fonte para esse projeto no seu servidor da Web!

Observe também que, se você criar o seu site para se executado localmente em um navegador (como veremos na seção Depuração a seguir), poderá obter um erro por adicionar uma referência a System.Runtime. Para corrigir isso, abra o arquivo web.config e altere o elemento compilation para que fique desta maneira:

 <compilation debug="true" targetFramework="4.0">
  <assemblies>
    <add assembly="System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> 
  </assemblies>
</compilation>

Depois de tudo isso, vejamos uma página (chamada DefaultNE.aspx) que produz a mesma saída que o exemplo embutido em código anterior:

 <?xml version="1.0" encoding="utf-8" ?>
<%@ Page Language="C#" %>
<script runat="server">   1:  
   2:     public string GenerateTileXML()
   3:     {
   4:         // Construct the square template
   5:         NotificationsExtensions.TileContent.ITileSquarePeekImageAndText02 squareTile = 
   6:             NotificationsExtensions.TileContent.TileContentFactory.CreateTileSquarePeekImageAndText02();
   7:         squareTile.Branding = NotificationsExtensions.TileContent.TileBranding.None;        
   8:         squareTile.Image.Src = "https://www.kraigbrockschmidt.com/images/Liam07.png";
   9:         squareTile.TextHeading.Text = "Liam--";
  10:         squareTile.TextBodyWrap.Text = "Giddy on the day he learned to sit up!";
  11:         
  12:         // Construct the wide template
  13:         NotificationsExtensions.TileContent.ITileWideSmallImageAndText04 wideTile =
  14:             NotificationsExtensions.TileContent.TileContentFactory.CreateTileWideSmallImageAndText04();
  15:         wideTile.Branding = NotificationsExtensions.TileContent.TileBranding.None;
  16:         wideTile.Image.Src = "https://www.kraigbrockschmidt.com/images/Liam08.png";
  17:         wideTile.TextHeading.Text = "This is Liam";
  18:         wideTile.TextBodyWrap.Text = "Exploring the great outdoors!";
  19:                 
  20:         // Attach the square template to the notification
  21:         wideTile.SquareContent = squareTile;
  22:         wideTile.Lang = "en-US";
  23:         
  24:         // The wideTile object is an XMLDOM object, suitable for issuing tile updates
  25:         // directly. In this case we just want the XML text.
  26:         return wideTile.ToString();        
  27:     }

</script>
<%

   1:  = GenerateTileXML() 

%>

Você pode visitar esse serviço em https://programmingwin8-js-ch13-hellotiles.azurewebsites.net/DefaultNE.aspx, onde você obterá basicamente o mesmo XML de antes, com pequenas diferenças. Colando o URI no cenário 4 de Exemplos de notificações periódicas e por push do cliente também gerará as mesmas atualizações de bloco de antes.

Depurando serviços

Se você gerou uma atualização de bloco ou notificação, obviamente, significa que o XML de resposta do serviço foi adequadamente formado, porque o Windows rejeita todo XML que não for. Esse é um bom motivo para usar a biblioteca NotificationsExtensions já que ela reduz significativamente os erros que você pode cometer nesse processo.

Mas e se o serviço não funcionar bem? Como você pode diagnosticar e depurar como ele está manipulando solicitações e gerando sua resposta?

Na verdade, quando experimentei pela primeira vez o código do serviço ASP.NET acima, as atualizações não apareceram porque havia uma nova linha à esquerda na parte superior da resposta XML. É exatamente por isso que o cabeçalho <?xml ?> aparece na primeira linha do arquivo e não depois da diretiva <%@ Page %> , e não há nenhum feed de linha extra.

Sem dúvida, há vários motivos para você não querer mexer no código do seu serviço e depurar linha por linha, especialmente se estiver consultando um banco de dados e processando todos os resultados da consulta.

O truque para fazer isso é usar o localhost no seu computador de desenvolvimento, que permite executar e depurar o serviço localmente ao mesmo tempo em que você executa o código de teste do cliente, como os exemplos do SDK.

Ter um localhost significa que você está executando um servidor Web local como os Serviços de Informações da Internet ou Apache. Para ativar o IIS no Windows (ele é interno), vá para Painel de Controle > Ativar ou desativar recursos do Windows. Marque a caixa Serviços de Informações da Internet no nível superior, como mostrado abaixo, para instalar os principais recursos:

Caixa de diálogo Recursos do Windows com Serviços de Informações da Internet marcado

Depois do IIS ser instalado, o site local tratado pelo https://localhost/ se encontra na pasta c:\inetpub\wwwroot. É aqui que você coloca coisas como a página PHP descrita na última seção para poder usar um URI como https://localhost/dayofmonthservice.php nos exemplos de cliente.

Para usar o PHP com IIS, talvez seja preciso instalá-lo com o Web platform installer da Microsoft para que o código do servidor seja executado adequadamente. Depois da instalação do PHP, experimente inserir um URI para uma página PHP local no seu navegador. Se você receber uma mensagem de erro dizendo: "O manipulador PHP53_via_FastCGI tem um módulo incorreto�� (sim, isso é muito útil!), retorne para a caixa de diálogo Ativar ou desativar recursos do Windows mostrada anteriormente, vá para Serviços de Informações da Internet > Serviços da World Wide Web > Recursos de Desenvolvimento de Aplicativos, marque a caixa CGI e pressione OK. Depois de instalar o mecanismo CGI, sua página PHP funcionará.

Com o localhost pronto, você pode depurar os serviços no seu próprio computador usando o Visual Studio Express para Web ou o Visual Studio Ultimate. Você também pode usar o Visual Studio Express para Web com o Visual Studio Express para Windows para depurar o código de cliente e servidor juntos.

Quando você usa um serviço ou site no depurador do Visual Studio (para Web), ele é executado no navegador em uma URL como https://localhost:<port>/ onde <port> é atribuído aleatoriamente para esse projeto. Por exemplo, quando executo a página DefaultNE.aspx da seção anterior no Visual Studio Express para Web, essa página abre no Internet Explorer com o URI de https://localhost:52568/HelloTiles/DefaultNE.aspx. Se eu defini um ponto de interrupção no código dessa página, o depurador para imediatamente nesse ponto.

Os pontos de interrupção também são atingidos se você usar o mesmo URI de localhost no código do cliente para iniciar solicitações. Então, por exemplo, se eu executar o Exemplos de notificações periódicas e por push do cliente no Visual Studio Express para Windows e colar o URI no cenário 4, o Visual Studio Express para Web interromperá meu serviço no depurador assim que o Windows fizer uma solicitação. Então poderei entrar nesse código (felizmente o Windows é paciente) e verificar se a resposta certa está sendo gerada. Se não estiver, posso corrigir o código e reiniciar o serviço no localhost.

Se você tiver certeza de que seu serviço está funcionando conforme esperado, poderá carregá-lo no seu host da Web online (ou ambiente de teste) e realizar os testes de produção finais.

Observe que não é necessário executar o código de cliente em um depurador para usar o localhost dessa forma. Se você fizer isso, porém, existe uma opção no Visual Studio que deve ser ativada para que o localhost funcione. Ela é marcada por padrão, mas se você precisar alterá-la, a encontrará nas propriedades do seu projeto em Depuração > Permitir o Loopback de Rede Local:

A opção Permitir o Loopback de Rede Local

Dados de fontes externas

Além de consultar seu próprio banco de dados, é totalmente possível para um serviço de notificação periódica enviar solicitações para outros serviços a fim de obter dados para sua resposta. O problema, porém, é que essas solicitações são assíncronas por natureza e sujeitas a várias condições de falha. Por isso, seu uso pode complicar muito a implementação do seu serviço.

Para simplificar, podemos aproveitar o fato de que o Windows só faz solicitações para o serviço em intervalos de 30 minutos ou mais, conforme imposto pela API cliente. Isso significa que há bastante tempo para outros processos do lado do servidor poderem fazer solicitações externas para monitorar alertas do tempo, resultados de líder, RSS feeds e qualquer coisa para a qual exista uma API da Web. Esses processos armazenam resultados no seu banco de dados, que estão prontos para o seu serviço de notificação consultar (de forma síncrona) quando receber sua próxima solicitação.

Na verdade, os agentes podem atualizar o mesmo banco de dados. Por exemplo, os usuários podem inserir dados pelo seu site. Eles podem usar aplicativos de telefone celular para controlar suas atividades e os resultados são carregados automaticamente em um banco de dados. O banco de dados também pode ser atualizado por amigos que usam o mesmo aplicativo em seus dispositivos individuais.

Essa combinação é mostrada abaixo, onde o banco de dados funciona como um armazenamento central para o estado do back-end e o serviço de notificação periódica funciona como um consumidor simples desse estado.

database_servers_graph

Usando os Serviços Móveis do Windows Azure com serviços de atualização periódica

Quando você começar a entender e a criar com serviços de back-end para dar suporte a blocos dinâmicos e outras notificações, aproveite para explorar os Serviços Móveis do Windows Azure, que chamarei de AMS para encurtar. Além de simplificar significativamente as notificações por push, como veremos na parte 3 desta série, o AMS pode ser usado para dar suporte a serviços de atualizações periódicas de várias maneiras:

  • Em um serviço móvel, você pode criar tarefas em segundo plano agendadas que podem fazer solicitações para outros serviços e armazenar os resultados no seu banco de dados. Um exemplo disso, para obter tweets do Twitter, pode ser encontrado no tópico Schedule recurring jobs in Mobile Services (Agendar tarefas recorrentes nos Serviços Móveis).
  • Quando você cria um banco de dados do SQL Server no AMS (ou em outro lugar no Windows Azure), ele pode ser acessado como qualquer outro banco de dados do SQL Server hospedado na Web, por isso você pode usá-lo nos sites e em outros serviços, inclusive naqueles escritos em PHP.
  • O AMS facilita muito a inserção de registros em um banco de dados de um aplicativo cliente, usando o SDK dos Serviços Móveis.
  • Independentemente dos serviços móveis, o Windows Azure pode hospedar processos do servidor escritos em diversas linguagens, inclusive Node.js, Python, Java, PHP e .NET.

No futuro, veja também um novo recurso chamado "operações de serviço" nos Serviços Móveis do Azure que permite criar pontos de extremidade http arbitrários, como um serviço de notificação periódica.

Para obter mais informações sobre o Windows Azure, visite https://www.windowsazure.com. Para obter os vídeos de apresentação, visite também Windows Azure Mobile Services Tutorials (Tutoriais de Serviços Móveis do Windows Azure) no Channel 9.

Agora que já vimos como criar os serviços de notificação periódica, estamos prontos para passar para a próxima etapa que são as notificações por push. As notificações por push são necessárias quando você precisa lançar atualizações com mais frequência do que poderia com as notificações periódicas, basicamente sob demanda (por isso a palavra "push"). Esse será o tópico da parte 3 desta série, onde veremos muito mais dos Serviços Móveis do Azure.

Kraig Brockschmidt

Gerente de programas, equipe de ecossistema do Windows

Autor de Programming Windows 8 Apps in HTML, CSS, and JavaScript (Programando aplicativos do Windows 8 em HTML, CSS e JavaScript)