Algumas vezes amigos já me questionaram sobre o uso de Hypermedia em protocolos REST. É sem dúvida um assunto que me agrada bastante, e acho que a discussão pode trazer boas idéias para quem implementar serviços REST interessantes.
Começando do começo: o que é Hypermedia?
Hypermedia é uma extensão lógica de Hypertext, na qual conseguimos combinar gráficos, áudio, vídeo, texto plano e hyperlinks para criar um fluxo de navegação não-linear. (Definição retirada da Wikipedia).
Motivação: REST sem Hypermedia
No artigo de REST que escrevi no começo de 2008 eu falei sobre diversos aspectos importantes em um protocolo de comunicação REST. Recomendo a leitura para quem ainda não tenha visto o artigo. Naquele artigo, cobri praticamente todos os detalhes aos quais devemos prestar atenção – a exceção ficou por conta de Hypermedia Depósitos e Saques.
Para ilustrar uma resposta REST sem uso de Hypermedia, copio aqui uma listagem que usei no artigo de REST da Java Magazine.
HTTP/1.1 200 OK
Date: nnn
Content-Type: text/xml; charset=utf-8
Content-Length: nnn
Last-Modified: Sat, 19 Feb 2008 13:40:03 GMT
<?xml version="1.0" ?>
<ofertasDoItem>
<oferta>
<codOferta>23c32440-959a-4c33-b6ee-caff8e41422a</codOferta>
<valor>111.22</valor>
<dataModificacao>2008-02-05</dataModificacao>
<item>
<codItem>b7d24e54-0c0d-4fa0-b4fe-d7cf60180ed7</codItem>
<novo>false</novo>
<vendido>false</vendido>
</item>
<ofertante>
<codUsuario>505ab9d4-4b4b-4ce6-b128-b7408268126c</codUsuario>
</ofertante>
<vencedora>false</vencedora>
</oferta>
<oferta>
<codOferta>41ffac11-5ba2-405b-acc8-8e3d0904b158</codOferta>
<valor>111.22</valor>
<dataModificacao>2008-02-14</dataModificacao>
<item>
<codItem>b7d24e54-0c0d-4fa0-b4fe-d7cf60180ed7</codItem>
<novo>false</novo>
<vendido>false</vendido>
</item>
<ofertante>
<codUsuario>505ab9d4-4b4b-4ce6-b128-b7408268126c</codUsuario>
</ofertante>
<vencedora>false</vencedora>
</oferta>
</ofertasDoItem>
Se você julga que isso parece um grafo de objetos serializado, você não está enganado, é exatamente isso.
O que temos a ganhar com Hypermedia?
Quando implementamos a API REST da autenticação/autorização/cadastro da Globo.com, o protocolo REST sem Hypermedia atendeu perfeitamente. A comunicação funcionou, permitiu integração de clientes de todas as plataformas, de forma desacoplada tecnologicamente. No que será que Hypermedia poderia ajudar?
O cerne do benefício de Hypermedia está no conhecimento prévio que os clientes devem ter sobre o seu protocolo. Repare no seguinte fragmento da resposta:
<item>
<codItem>b7d24e54-0c0d-4fa0-b4fe-d7cf60180ed7</codItem>
<novo>false</novo>
<vendido>false</vendido>
</item>
O que podemos dizer sobre ele? Ele traz informações “quase” completas sobre o item em questão. Todos os atributos estão presentes, porém não dizemos nada ao cliente sobre como interagir com este recurso, não é verdade? Um cliente que receba este XML conseguirá manipulá-lo SE JÁ SOUBER PREVIAMENTE COMO! O cliente já deve conhecer as URIs de cada recurso e saber quais manipulações ele pode fazer sobre os mesmos.
Como Hypermedia pode nos levar além? Com Hypermedia podemos dizer aos clientes como manipular os recursos, e com isso diminuir a inteligência embutida nos clientes.
Exemplo de resposta usando Hypermedia
Agora pegarei um trecho do meu artigo sobre AtomPub para exemplificar uma resposta com uso de Hypermedia:
<?xml version="1.0" encoding="UTF-8"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:apps="http://schemas.google.com/apps/2006"
xmlns:gd="http://schemas.google.com/g/2005">
<atom:id>https://www.google.com/a/feeds/example.com/user/2.0/SusanJones</atom:id>
<atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
<atom:category scheme="http://schemas.google.com/g/2005#kind"
term="http://schemas.google.com/apps/2006#user"/>
<atom:title type="text">SusanJones</atom:title>
<atom:link rel="self" type="application/atom+xml"
href="https://www.google.com/a/feeds/example.com/user/2.0/SusanJones"/>
<atom:link rel="edit" type="application/atom+xml"
href="https://www.google.com/a/feeds/example.com/user/2.0/SusanJones"/>
<apps:login userName="SusanJones" suspended="false" admin="false" changePasswordAtNextLogin="false" agreedToTerms="true"/>
<apps:name familyName="Jones" givenName="Susan"/>
<gd:feedLink rel="http://schemas.google.com/apps/2006#user.nicknames"
href="https://www.google.com/a/feeds/example.com/nickname/2.0?
username=Susy-1321"/>
<gd:feedLink rel="http://schemas.google.com/apps/2006#user.emailLists"
href="https://www.google.com/a/feeds/example.com/emailList/2.0?
recipient=us-sales@example.com"/>
</atom:entry>
Qual é a principal diferença aqui? Através do uso de links conseguimos indicar uma URI para manipulação do recurso, e através do atributo rel, indicamos QUAL é a manipulação. Algumas coisas ainda ficam implícitas. Por exemplo, se o link indica um rel=”edit”, a manipulação em questão provavelmente será feita com uma requisição HTTP PUT. Se o link tiver um rel=”show”, a manipulação deve ser uma leitura, feita com HTTP GET.
Hypermedia x Acoplamento
Neste ponto deve ter ficado claro que o uso de Hypermedia confere maior significado ao protocolo de comunicação. Em vez de trafegar dados crus, enviamos também metadados que permitem a construção de clientes mais genéricos dos nossos serviços.
Reduzindo o conhecimento que os clientes têm sobre o servidor, naturalmente o acoplamento diminui. O servidor precisa manter fixa apenas a URI inicial da conversação, pois as posteriores serão informadas durante a comunicação.
O servidor pode também utilizar múltiplos tipos de conteúdo, de forma que alguns clientes consigam interagir de forma mais ampla. Um exemplo seria um IPod que sabe consumir podcasts, enquanto estes seriam ignorados por um browser comum.
Precisamos de Hypermedia em um protocolo REST?
Resposta curta: na minha opinião não.
Resposta longa: espero que minha resposta curta não tenha deixado a impressão de que não valorizo Hypermedia. Muito pelo contrário, acho que um protocolo que use Hypermedia será muito elegante e terá baixo acomplamento.
Entretanto, construir protocolos para clientes genéricos é bem mais difícil. Em alguns casos a decisão de diminuir o acoplamento aumenta na mesma proporção a complexidade do protocolo de comunicação envolvido.
É perfeitamente possível criar um excelente protocolo de comunicação sem Hypermedia. Você poderá ter clientes de múltiplas plataformas, conversando com seu servidor em múltiplos formatos de conteúdo.
Há um ponto de atenção no quanto seus clientes conhecem sobre seus formatos de URIs. Sem Hypermedia, mudanças de URIs podem quebrar seus clientes, o que traz um nível de acoplamento maior do que o existente com Hypermedia.
Recomendação geral: o uso de Hypermedia é consistente com a arquitetura da web, utilizada há muitos anos com sucesso. Quando um protocolo de comunicação REST faz uso de Hypermedia, o acoplamento fica baixo e nos dá bastante liberdade para evolução. O ideal é tentar usar Hypermedia ao máximo desde o começo, mas sempre ponderando o nível de complexidade na construção dos clientes.
xml version=”1.0″ encoding=”UTF-8″?>
<atom:entry xmlns:atom=”http://www.w3.org/2005/Atom”
xmlns:apps=”http://schemas.google.com/apps/2006″
xmlns:gd=”http://schemas.google.com/g/2005″>
<atom:id>https://www.google.com/a/feeds/example.com/user/2.0/SusanJones</atom:id>
<atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
<atom:category scheme=”http://schemas.google.com/g/2005#kind”
term=”http://schemas.google.com/apps/2006#user”/>
<atom:title type=”text”>SusanJones</atom:title>
<atom:link rel=”self” type=”application/atom+xml”
href=”https://www.google.com/a/feeds/example.com/user/2.0/SusanJones”/>
<atom:link rel=”edit” type=”application/atom+xml”
href=”https://www.google.com/a/feeds/example.com/user/2.0/SusanJones”/>
<apps:login userName=”SusanJones” suspended=”false” admin=”false” changePasswordAtNextLogin=”false” agreedToTerms=”true”/>
<apps:name familyName=”Jones” givenName=”Susan”/>
<gd:feedLink rel=”http://schemas.google.com/apps/2006#user.nicknames”
href=”https://www.google.com/a/feeds/example.com/nickname/2.0?
username=Susy-1321″/>
<gd:feedLink rel=”http://schemas.google.com/apps/2006#user.emailLists”
href=”https://www.google.com/a/feeds/example.com/emailList/2.0?
recipient=us-sales@example.com”/>
</atom:entry>
Recent Comments