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:
|
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: