SilverLight 4 - Usando REST e WCF Data Services para interagir com o Flickr (C#)


Este artigo mostra como interagir com o Flickr usando REST e WCF DataServices em uma aplicação SilverLight 4.(Ele foi baseado em diversos exemplos encontrados na web com adaptações para rodar no Visual Web Developer 2010 Express)

O que é REST ?

O termo REST- Representation State Transfer, se referia originalmente a um conjunto de princípios de arquitetura (descritos mais abaixo), na atualidade se usa no sentido mas amplo para descrever qualquer interface web simples que utiliza XML e HTTP (ou YAML, JSON, ou texto puro), sem as abstrações adicionais dos protocolos baseados em padrões de trocas de mensagem como o protocolo de serviços web SOAP. É possível desenhar sistemas de serviços web de acordo com o estilo arquitetural REST descrito por Fielding, e também é possível desenhar interfaces XMLHTTP de acordo com o estilo de RPC mas sem utilizar SOAP. Estes usos diferentes do termo REST causam certa confusão em discussões técnicas, onde RPC não é um exemplo de REST.

Os sistemas que seguem os princípios REST são freqüentemente chamados de RESTful; os defensores mais ferrenhos do REST são chamados pelos mesmos de RESTafarianos

REST afirma que a web já desfrutou de escalabilidade como resultado de uma série de desenhos fundamentais chaves:

  • Um protocolo cliente/servidor sem estado: cada mensagem HTTP contém toda a informação necessária para compreender o pedido. Como resultado, nem o cliente e nem o servidor necessitam gravar nenhum estado das comunicações entre mensagens. Na prática, muitas aplicações baseadas em HTTP utilizam cookies e outros mecanismos para manter o estado da sessão (algumas destas práticas, como a reescrita de URLs, não são permitidas pela regra do REST).
  • Um conjunto de operações bem definidas que se aplicam a todos os recursos de informação: HTTP em si define um pequeno conjunto de operações, as mais importantes são POST, GET, PUT e DELETE. Com freqüência estas operações são combinadas com operações  CRUD para a persistência de dados, onde POST não se encaixa exatamente neste esquema.
  • Uma sintaxe universal para identificar os recursos. No sistema REST, cada recurso é unicamente direcionado através da sua URI (Uniforme Resource Identifiers).
  • O uso de hipermídia, tanto para a informação da aplicação como para as transições de estado da aplicação: a representação deste estado em um sistema REST são tipicamente HTML ou XML. Como resultado disto, é possível navegar com um recurso REST a muitos outros, simplesmente seguindo ligações sem requerer o uso de registros ou outra infra-estrutura adicional. (http://pt.wikipedia.org/wiki/REST)

O que é o Flickr ?

O Flickr é um site popular onde as pessoas podem fazer upload e compartilhar imagens e vídeos. Além de ver esse conteúdo no site, o Flickr oferece uma ampla gama de serviços para a interação com seu conteúdo. Atualmente o Flickr tem milhões de usuários que compartilham bilhões de imagens e podemos usar todo esse acervo através da facilidade que a API do Flickr fornece dentro de nossas aplicações para prover mais funcionalidade para nossos usuários finais.

Antes de continuar você deve obter uma chave de identificação para poder executar o exemplo deste artigo. Abaixo vemos a tela de acesso a solicitação de chave feita no endereço:  http://www.flickr.com/services/api/keys/

A maioria dos sites que expõem serviços públicos, tais como Flickr, Amazon, Digg  permitem-nos o livre acesso aos seus serviços, porém muitas vezes você precisa se registrar para obter uma identificação ou  chave.

Para  executar o  código deste artigo, você precisará de uma chave para acessar a API do Flickr, que pode ser obtida gratuitamente a partir de http://www.flickr.com/services/api/keys/

Após você responder algumas perguntas você poderá solicitar uma chave e após recebê-la deverá usá-la conforme veremos mais adiante.

Quais os recursos necessários que devemos ter instalados:

1 - Precisamos ter instalado o Visual Studio 2010 ou o Visual Web Developer Express 2010 ;
2 - Precisamos ter instalado o SilverLight 4 Tools for Visual Studio 2010;
3 - Precisamos ter instalado o  SilverLight 4 ToolKit (april 2010)

Obs: Para o exemplo mostrado neste artigo eu estou usando o Visual Web Developer 2010 Express Edition.

Objetivo : Criar uma aplicação SilverLight 4 que permite pesquisar fotos no Flickr com base em um critério informado pelo usuário;

premissas:

- Permitir ao usuário informar um critério de busca na aplicação SilverLight para obter fotos no Flickr;
- Permitir ao usuário clicar em um dos resultados apresentados para obter detalhes da foto;
- Utilizar os métodos flickr.photos.search e flickr.photos.getinfo da API do Flickr usando REST e WCF DataServices;
- Definir uma aplicação SilverLight que será usada como interface com o usuário;

Abra o Visual Web Developer 2010 Express Edition e crie um novo projeto com o nome SilverLight4_Fickr usando a linguagem C#;

No menu File-> New Project selecione Visual Basic -> SilverLight e a seguir o template SilverLight Application informando o nome SilverLight4_Fickr ;

Clique em OK;

Na seguinte janela de diálogo apenas confirme as opções e clique em OK;

Será criado uma solução com dois projetos: O projeto SilverLight e o Projeto Web;

Vamos iniciar definindo a interface com o usuário no arquivo MainPage.xaml.

Ela será bem simples: teremos um StackPanel no topo da página, contendo uma imagem, um TextBox para introduzir a consulta de pesquisa e um botão. A página também contém um ScrollViewer com um WrapPanel (este controle faz parte do Silverlight Toolkit) à esquerda.

Conforme o leiaute abaixo:

Por isso no menu Project selecione Add Reference e na guia .NET selecione os componentes: System.Windows.ControlsToolKit conforme a figura abaixo:

O código XAML gerado é dado a seguir:

<UserControl xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" 
             xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
             x:Class="Silverlight_Flickr.MainPage" Background="LightGray"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:local="clr-namespace:Silverlight_Flickr"
            >
    <!--<UserControl.Resources>
        <local:ImageConverter x:Key="imageConverter"></local:ImageConverter>
    </UserControl.Resources>-->
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" Orientation="Horizontal" Grid.ColumnSpan="2">
            <Image Source="flickr.png" Stretch="None" Margin="3 0 0 0" ></Image>
            <TextBox x:Name="txtCriterio" Width="200" Height="30" Margin="5"></TextBox>
            <Button x:Name="btnProcurar" Content="Procurar no FLickr" Click="btnProcurar_Click" HorizontalAlignment="Center" 
VerticalAlignment="Center" Margin="5"></Button>
        </StackPanel>
        <ScrollViewer Grid.Row="1" Grid.Column="0" Background="DarkGray">
            <controlsToolkit:WrapPanel x:Name="ResultPanel"   HorizontalAlignment="Center"></controlsToolkit:WrapPanel>
        </ScrollViewer>
        <Grid x:Name="DetailGrid" Grid.Row="1" Grid.Column="1" Background="LightGray">
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width=".7*"></ColumnDefinition>
                <ColumnDefinition Width=".3*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Image x:Name="DetailImage" Source="{Binding ImageUri, Converter={StaticResource imageConverter}}" VerticalAlignment="Top" Margin="5"></Image>
            <Grid x:Name="ImageDetailGrid" Grid.Column="1" VerticalAlignment="Top" Margin="5" Visibility="Collapsed">
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <TextBlock Text="Detalhes da Imagem" FontSize="25" Grid.Row="0" FontWeight="Bold"></TextBlock>
                <TextBlock x:Name="ImageTitleTextBlock" Text="{Binding Titulo}" Grid.Row="1" FontSize="18" FontWeight="Bold"></TextBlock>
                <StackPanel Grid.Row="2">
                    <TextBlock Text="Data:"></TextBlock>
                    <TextBlock x:Name="DateTakenTextBlock" Text="{Binding Data}"  FontWeight="Bold" Foreground="#555555"></TextBlock>
                </StackPanel>
                <StackPanel Grid.Row="3">
                    <TextBlock Text="Descrição:"></TextBlock>
                    <TextBlock x:Name="DescriptionTextBlock" Text="{Binding Descricao}"  FontWeight="Bold" Foreground="#555555"></TextBlock>
                </StackPanel>
                <StackPanel Grid.Row="4">
                    <TextBlock Text="Informação:"></TextBlock>
                    <HyperlinkButton x:Name="MoreInfoTextBlock" Content="{Binding PhotoPageUrl}" NavigateUri="{Binding PhotoPageUrl}"  
FontWeight="Bold" Foreground="#555555"></HyperlinkButton>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>

 

Como vamos usar os serviços REST, vamos utilizar a classe WebClient. Esta classe existe no namespace System.Net, que é parte do assembly System.NET. Se você estiver usando o Visual Studio 2008, você vai precisar adicionar uma referência a este namespace. O Visual Studio 2010 já referencia este namespace automaticamente para SilverLight 3 e SilverLight 4.

A classe WebClient fornece métodos comuns para enviar ou receber dados de qualquer local, intranet ou recurso da Internet identificado por uma URI. Essa classe usa a classe WebRequest para fornecer acesso aos recursos sendo que instâncias WebClient podem acessar dados em qualquer WebRequest descendentes registrados com o método WebRequest.RegisterPrefix.

Como já mencionei , a API do Flickr é uma API REST. Assim, será preciso enviar uma solicitação para uma URI específica e ler a resposta retornada.

Vamos primeiro dar uma olhada na URI. Conforme definido pelo Flickr, esta URI precisa estar em um formato específico. Como nós vamos fazer uma pesquisa, vamos usar o método flickr.photos.search da API que requer dois parâmetros:  sua chave pessoal para acessar a API (chave_API)  e o critério de pesquisa inserido no campo de busca (text={1}).

Abaixo vemos um exemplo de código que realiza busca no Flickr usando um critério definido pelo usuário:

string chave_API = "123456"; //informe aqui a sua chave de acesso a API
string BuscaUrl = "
http://api.flickr.com/services/rest/?method=flickr.photos.search&
chave_API={0}&text={1}";

O código que obtém detalhes de uma imagem selecionada é o seguinte:

String detalhesUrl = "http://api.flickr.com/services/rest/?method=flickr.photos.getinfo&chave_API={0}&photo_id={1}";

Temos agora as URI, podemos usá-las para enviar uma solicitação usando REST. Para enviar os pedidos,vamos usar a classe WebClient novamente. No manipulador de eventos Click do botão - Procurar no Flickr, vamos criar uma instância dessa classe. Precisamos também registrar o método de retorno através de DownloadStringCompleted e enviar o pedido através DownloadStringAsync, passando a URI como um parâmetro. Tal como acontece com outros serviços, essas chamadas são assíncronas.

O evento DownloadStringCompleted ocorre quando uma operação de download de recurso é completada sendo que este evento é disparado a cada vez em uma operação assíncrona para baixar um recurso. Estas operações são iniciadas chamando o o método DownloadStringAsync.

Abra o arquivo MainPage.xaml.cs e defina os seguintes namespace no mesmo:

using System;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Windows.Media;

using System.Xml.Linq;

using System.Windows.Media.Imaging;

A seguir vamos declarar as variáveis que usaremos no projeto incluindo a chave da api e as URl de busca e detalhes de imagens da API do Flickr conforme abaixo:

string api_key = "XXXXXXXXXXXXXXXXXXXXXXXX";//informe a sua chave aqui

string searchUrl = "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={0}&text={1}";

string imageUrl = "http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg";

string detailUrl = "http://api.flickr.com/services/rest/?method=flickr.photos.getinfo&api_key={0}&photo_id={1}";


No evento Click do botão - Procurar no Flick - declare o código abaixo:

 

  private void SearchButton_Click(object sender, RoutedEventArgs e)
  {
            ResultPanel.Children.Clear();

            WebClient client = new WebClient();
            client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
            client.DownloadStringAsync(new Uri(string.Format(searchUrl, api_key, SearchTextBox.Text)));
 }

No evento client_DownloadStringCompleted vamos definir o código para obter as imagens selecionadas no Flickr;

No retorno, temos acesso ao resultado da chamada por meio da propriedade de Result na instância do DownloadStringCompletedEventArgs.

A resposta é um simples XML, formatado pelo Flickr em um formato específico. Nós vamos usar o LINQ para XML para analisar este XML código e criar uma lista de objetos ImageInfo (mostrado no código a seguir), um tipo definido para ter acesso tipado aos nossos dados no aplicativo do Silverlight. Note-se que a implementação ImageUrl cria o link para a imagem, usado pelo Flickr.

Vamos então incluir a seguinte classe para o projeto Silverlight. No menu Project->Add Class informe o nome ImageInfo.cs e digite o código a seguir nesta classe:


    
namespace SilverFlickr
{
    public class ImageInfo
    {
        public string ImageId { get; set; }
        public string FarmId { get; set; }
        public string ServerId { get; set; }
        public string Secret { get; set; }

        public string ImageUrl
        {
            get
            {
                return string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}_m.jpg", FarmId, ServerId, ImageId, Secret);
            }
        }
    }
}

 

Para tratar os detalhes da imagem vamos incluir a classe imageDetail.cs no projeto SilverLight:

using System;

namespace SilverFlickr
{
    public class ImageDetail
    {
        public string ImageId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public DateTime DateTaken { get; set; }
        public string PhotoPageUrl { get; set; }
        public Uri ImageUri { get; set; }
    }
}

Por fim, cada instância ImageInfo é usada para criar dinamicamente uma imagem e adicioná-la à WrapPanel. Cada imagem também recebe um evento de clique ligado a ele, que é utilizado para abrir a página de detalhes. Isso é mostrado no código a seguir:

  void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
  {
            XDocument xml = XDocument.Parse(e.Result);

            var photos = from results in xml.Descendants("photo")
                         select new ImageInfo
                         {
                             ImageId = results.Attribute("id").Value.ToString(),
                             FarmId = results.Attribute("farm").Value.ToString(),
                             ServerId = results.Attribute("server").Value.ToString(),
                             Secret = results.Attribute("secret").Value.ToString()
                         };

            foreach (var image in photos)
            {
                Image img = new Image();
                BitmapImage bmi = new BitmapImage(new Uri(image.ImageUrl, UriKind.Absolute));
                img.Source = bmi;
                img.Width = 200;
                img.Height = 200;
                img.Stretch = Stretch.Uniform;
                img.Tag = image;
                img.Margin = new Thickness(3);
                img.HorizontalAlignment = HorizontalAlignment.Center;
                img.MouseLeftButtonDown += new MouseButtonEventHandler(Thumbnail_Click);
                ResultPanel.Children.Add(img);
            }
  }

Para converter a imagem vamos incluir também a classe ImageConverter.cs com o código abaixo:

using System;
using System.Windows.Data;
using System.Windows.Media.Imaging;

namespace SilverFlickr
{
    public class ImageConverter:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null)
                return new BitmapImage((Uri)value);
            else
                return "";
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Isso é necessário pois o Binding XAMl espera um  BitmapImage, e a conversão do tipo A para tipo B é feito através do uso de uma classe de conversor que
implementa a interface IValueConverter. Essa interface tem duas
métodos: Convert e ConvertBack.

Finalmente para tratar os detalhes de uma imagem selecionada vamos definir os seguintes eventos:

   private void Thumbnail_Click(object sender, RoutedEventArgs e)
   {
            Image img = sender as Image;
            ImageInfo imageInfo = img.Tag as ImageInfo;
            ImageDetailGrid.Visibility = Visibility.Visible;
            if (imageInfo != null)
            {
                WebClient detailClient = new WebClient();
                detailClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(detailClient_DownloadStringCompleted);
                detailClient.DownloadStringAsync(new Uri(string.Format(detailUrl, api_key, imageInfo.ImageId)));
            }
        }

        void detailClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            XDocument xmlDocument = XDocument.Parse(e.Result);
            ImageDetail imgDetail = new ImageDetail();
            XElement photoElement;

            if (xmlDocument.Descendants("photo").Count<XElement>() > 0)
            {
                photoElement = xmlDocument.Descendants("photo").First<XElement>();
                
                imgDetail.Title = photoElement.Descendants("title").First<XElement>().Value;
                imgDetail.DateTaken = DateTime.Parse(photoElement.Descendants("dates").First<XElement>().Attribute("taken").Value);
                imgDetail.Description = photoElement.Descendants("description").First<XElement>().Value;
                imgDetail.PhotoPageUrl = photoElement.Descendants("urls").Descendants("url").First<XElement>().Value;

                //cria url para a imagem original
                string serverId = photoElement.Attribute("server").Value;
                string farmId= photoElement.Attribute("farm").Value;
                string imageId = photoElement.Attribute("id").Value;
                string secret;
                if (photoElement.Attribute("originalsecret") != null)
                {
                    secret = photoElement.Attribute("originalsecret").Value;
                    imgDetail.ImageUri = new Uri(string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}_o.jpg", farmId, serverId, imageId, secret));
                }
                else
                {
                    secret = photoElement.Attribute("secret").Value;
                    imgDetail.ImageUri = new Uri(string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg", farmId, serverId, imageId, secret));
                }
                DetailGrid.DataContext = imgDetail;
            }
       }

O formato do XML enviado pelos serviços do Flickr é fixo sendo desta forma seguro para construir o nosso código em torno da AP,I pois o formato pode ser considerado como um contrato entre o serviço e a aplicação cliente. O serviço será sempre retornar a resposta formatada de acordo com esta especificação.

A estrutura XML usada pelo Flickr é a seguinte:

<rsp>
<photos>
    <photo id="1234567890"
              secret="0987654321"
              server="1234"
              farm="1" />
</photos>
</rsp>

O Silverlight impõe restrições à comunicação com serviços externos. No entanto, permite que os acessemos serviços para permitir a conexão partir de um aplicativo Silverlight. A comunicação com os serviços expostos pelo Flickr a partir do Silverlight é possível porque o Flickr tem um arquivo crossdomain.xml no lugar que permite-nos fazer chamadas de qualquer domínio.

Executando o projeto iremos obter:

Simples , simples assim...

Pegue o projeto completo aqui:   Silverlight_Flickr.zip

Eu sei é apenas SilverLight 4 , mas eu gosto...

Referências:

José Carlos Macoratti