Feeds RSS e Atom no .NET

15 12 2009

Símbolo de Feed

Símbolo de Feed

Arquivos Feeds são listas de atualização de conteúdo de um determinado site, escritos nas especificações do padrão XML. Seu nome provém do inglês e significa alimentar, em alusão ao seu modo de utilização.

Feeds são um recurso indispensável em qualquer Website que forneça atualizações fequentes de conteúdo, pois possibilita que os leitores serem informados automaticamente sobre novas atualizações no site, sem a necessidade de realizar visitas periódicas para verificar se há novas informações disponíveis.

Sua utilização pode ser útil em sites com grandes quantidades de atualizações em seus conteúdos.

Atualmente, existem 3 padrões estabelecidos para a criação de feeds:

  • RSS 1.0 – RDF Site Summary 1.0 (RSS-DEV).
  • RSS 2.0 – Really Simple Syndication 2.0 (Userland).
  • Atom (IETF)

Neste post serão abordados os 2 padrões mais utilizados hoje em dia, o RSS 2.0 e o Atom 1.0. O Atom é o mais recente e veio para preencher as lacunas do RSS, porém seu objetivo é o mesmo do RSS: prover atualizações em um formato pré-definido. Para o usuário não existem grandes diferenças entre o uso do RSS ou do Atom.

Para a criação de feeds RSS ou Atom no .NET Framework 3.5 é necessária a utilização da  Biblioteca Syndication, no namespace System.ServiceModel.Syndication, que possui as classes necessárias para a criação de feeds RSS ou Atom.

Partindo do zero, será criado um projeto com o objetivo de publicar um feed para qualquer site. O projeto será criado no Visual Web Developer 2008. Será utilizado também Sql Server para o banco de dados e LINQ To Entities para manipulação dos dados.

Será necessário fazer o download do WCF REST Starter Kit. Para baixá-lo, clique aqui. Para a execução deste exemplo, é necessário possuir o Service Pack 1 do Visual Studio 2008. Caso você não possua, clique aqui para baixá-lo.

Com tudo instalado, vamos criar um projeto utilizando o template Atom Feed WCF Service ,como na imagem a seguir:

Criando Projeto Atom Feed WCF Service

Figura 01 - Criando Projeto Atom Feed WCF Service

A partir do projeto criado, podemos criar uma base de dados para armazenar as informações dos feeds. Para isso, basta clicar com o botão direito na pasta App_Data > Add > New Item  > SQL Server Database.

Criação do Banco de Dados AtomFeed

Criação do Banco de Dados AtomFeed

Clique no arquivo .mdf e com isso irá abrir no canto esquerdo a janela Server Explorer. Expanda o arquivo .mdf , clique com o botão direito em Tables > Add New Table. Crie uma tabela no seguinte padrão:

Criação da Tabela Feed

Salve o projeto. ele irá pedir pra nomear a tabela. No exemplo foi usado o Nome Feed. Para facilitar o acesso aos dados, será utilizado LINQ to Entities. Em uma próxima ocasião abordaremos com mais detalhes um artigo sobre LINQ e Entity Data Model.

Adiciona-se então um novo projeto com o template ADO.NET Entity Data Model, conforme figura abaixo:

Adicionando projeto ADO.NET Entity Data Model

Adicionando projeto ADO.NET Entity Data Model

Ao adicionar, irá abrir um Assistente para auxiliá-lo. Selecione então a opção Generate from Database:

Criação do Entity Data Model

Criação do Entity Data Model

Agora selecione a base de dados criada anteriormente, dessa maneira:

Configurando o Entity Data Model

Configurando o Entity Data Model

Selecionando a Base de Dados

Selecionando a Base de Dados

Assim,  é feito um mapeamento da tabela Feed automaticamente. São criados novos arquivos: AtomFeedModel.edmx e Service.svc, este ultimo criado automaticamente contendo a classe FeedService e o método GetFeed. O método já possui a lógica para geração de um feed Atom.  Porém, vamos utilizar outra lógica. Então, substitua o conteúdo do método GetFeed pelo bloco abaixo:

if (i < 0) throw new WebProtocolException(HttpStatusCode.BadRequest, "numItems cannot be negative", null);
if (i == 0) i = 1;

// Estanciando objeto Feed e setando suas propriedades

SyndicationFeed feed = new SyndicationFeed("Feed", "Exemplo de Feed",
new Uri("http://exemplo.com/feed"));
feed.Id = "http://exemplo.com/feed";
feed.Copyright = new TextSyndicationContent("Copyright ® 2010 Programeiro");
feed.Authors.Add(new SyndicationPerson("hsalvs@gmail.com", "Heitor", string.Empty));
feed.Language = "pt-BR";

//Lista contendo os feeds, criados de acordo com o banco de dados
List items = new List();

//Agora será utilizado LINQ to Entities.
//Acessando a base de dados para obter informações necessárias

using (AtomFeedEntities entities = new AtomFeedEntities())
{
var query = from item in entities.Feed
orderby item.DataCriacao descending
select item;

/*O LINQ não permite que um construtor com parêmetros seja chamado no 'select',
* então é preciso iterar cada item para criar o respectivo SybdicationItem */

foreach (var item in query)
{
/*Para cada item da base de dados criamos um SyndicationItem com
as respectivas propriedades*/
SyndicationItem syndicationItem = new SyndicationItem();
syndicationItem.Id = item.ID.ToString();
syndicationItem.Title = TextSyndicationContent.
CreatePlaintextContent(item.Título);
syndicationItem.Content = TextSyndicationContent.
CreateHtmlContent(item.Descricao);
syndicationItem.PublishDate = item.DataCriacao;
syndicationItem.AddPermalink(new Uri(string.Format("http://exemplo.com/item/{0}", syndicationItem.Id)));

//Agora adicionamos na lista
items.Add(syndicationItem);
}
}

//Atribui-se a lista ao feed, limitando ao número de itens especificado atraves da variavel numItems
feed.Items = items.Take(i);

WebOperationContext.Current.OutgoingRequest.ContentType = ContentTypes.Atom;

return new Atom10FeedFormatter(feed);

O método GetFeed possui um parêmetro que recebe o número de itens desejados pelo usuário naquele acesso. Foi aproveitada a mesma lógica de tratamento desse parâmetro do templete padrão. Após isso foi instanciada a classe SyndicationFeed e foram setadas as propriedades com as informações do feed contido no banco de dados. Em seguida, é populada uma lista de SyndicationItems que serão as atualizações do feed. No final, atribui-se a lista de items ao SyndicationFeed, limitando de acordo com a variavel numIrems e após isso é retornado um Atom10FeedFormatter baseado no nosso feed. A própria classe aplica a formatação definida para o padrão Atom 1.0, portanto é uma coisa a menos para se preocupar.

Popule o banco de dados que foi criado e depois verifique as informações no browser. Clique no item Service.svc para visualizar os resultados. Neste caso ele mostrará apenas o primeiro feed do banco porque a variável numItems equivale a 1.

Resultado no Firefox

Resultado no Firefox

O XML gerado pelo fica assim:


<feed xml:lang="pt-BR" xmlns="http://www.w3.org/2005/Atom">
 <title type="text">Feed</title>
 <subtitle type="text">Exemplo de Feed</subtitle>
 <id>http://exemplo.com/feed</id>
 <rights type="text">Copyright ® 2010 Programeiro</rights>
 <updated>2009-12-15T20:25:53Z</updated>
 <author>
   <name>Heitor</name>
   <email>hsalvs@gmail.com</email>
 </author>
 <link rel="alternate" href="http://exemplo.com/feed"/>
 <entry>
   <id>http://exemplo.com/item/1</id>
   <title type="text">Teste</title>
   <published>2009-12-15T00:00:00-02:00</published>
   <updated>2009-12-15T20:25:53Z</updated>
   <link rel="alternate" href="http://exemplo.com/item/1"/>
   <content type="html">Um teste de Atom</content>
 </entry>
</feed>

Agora para deixar esse código flexível, possibilitando a geração de feeds RSS e Atom, é fácil.
Precisamos apenasadicionar um novo parâmetro para informar o formato desejado e utilizar uma classe mais genérica apara que seja possível ao método entregar tanto RSS quanto Atom.
Além disso, vamos adicionar o atributo ServiceKnownTypeAttibute informando os tipos Rss20FeedFormatter e Atom10Formatter ao método, já que estes serão os dois tipos retornados pelo nosso método.
A assinatura ficará assim:

[WebHelp(Comment = "Sample description for GetFeed.")]
 [WebGet(UriTemplate = "?numItems={i}&format={format}")]
 [ServiceKnownType(typeof(Rss20FeedFormatter))]
 [OperationContract]
 [ServiceKnownType(typeof(Atom10FeedFormatter))]
 public SyndicationFeedFormatter GetFeed(int i, string format)

Foi alterada a assinatura do método GetFeed, adicionando a variável format, para que se estabeleça o padrão de retorno do feed. O tipo de retorno foi alterado para SyndicationFeedFormatter.
Portando, o retorno do método GetFeed ficará assim

if (format.ToLower() == "rss")
 return new Rss20FeedFormatter(feed, false);

 return new Atom10FeedFormatter(feed);

Agora vamos consumir um feed. Primeiramente,  adicionamos um Web Form ao projeto. O chamarei de Consumidor.aspx. Agora colocamos dois Labels e um DataList.

No arquivo .aspx , precisamos alterar o ItemTemplate do DataList para que seja possível exibir o conteúdo do feed. Então, substituímos o código do DataList pelo seguinte código:

<asp:DataList ID="DltFeed" runat="server">
<ItemTemplate>
<asp:HyperLink ID="hplFeed" runat="server" Text='<%# Eval("Title.Text")%>’ Font-Bold="true"
NavigateUrl='<%# Eval("Links[0].Uri.AbsoluteUri") %>’></asp:HyperLink>
<br />
<asp:Label ID="lblTexto" runat="server" Text='<%# Eval("Summary.Text") %>’></asp:Label>
</ItemTemplate>
</asp:DataList>

E no CodeBehind, dentro do método Page_Load devemos colocar:


using System.Xml;
using System.ServiceModel.Syndication;
...
 protected void Page_Load(object sender, EventArgs e)
 {
    XmlReader reader = XmlReader.Create("http://blogs.iis.net/rawmainfeed.aspx");
    Rss20FeedFormatter fmt = new Rss20FeedFormatter();
    fmt.ReadFrom(reader);
    reader.Close();
    Label1.Text = fmt.Feed.Title.Text;
    Label2.Text = fmt.Feed.Description.Text;
    DltFeed.DataSource = fmt.Feed.Items;
    DltFeed.DataBind();
 }

Utilizamos o Rss2.0 para ler o conteúdo do feed via HTTP e então a classe Rss20feedFormatter para interpretá-lo. Essa classe é responsável por realizar todo o ‘trabalho sujo’, pois todo o trabalho que temos é o de chamar o método ReadFrom(). Caso o feed a ser lido fosse Atom, a única alteração seria o uso da classe Atom10FeedFormatter no lufar da Rss20FeedFormatter.

O resultado é esse:

Leitor de Feeds

Leitor de Feeds

Fontes: