Construindo uma API RESTful em Java

Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
Confirmar voto
0
 (35)  (0)

Este artigo apresenta o estilo REST de construção de web services, bem como as melhores práticas comumente adotadas na criação de uma RESTful API.

Fique por dentro
Este artigo apresenta o estilo REST de construção de web services, bem como as melhores práticas comumente adotadas na criação de uma RESTful API. Seguindo as práticas analisadas aqui, o leitor poderá melhorar a performance, a manutenibilidade e a usabilidade das APIs projetadas, tornando-as completas e intuitivas para os desenvolvedores que as utilizarão.

Este tema é útil para desenvolvedores que precisem criar uma API pública ou privada para expor dados e funcionalidades de um sistema web para outras aplicações, sejam elas web, mobile ou desktop.

À medida que um sistema web vai evoluindo, é comum que se torne necessário uma nova forma de se comunicar com ele. Comumente, serão humanos que o usarão em um browser para acessar e modificar seus dados. Porém, pode chegar o momento em que desejamos tornar possível que outras aplicações se comuniquem com nosso sistema, seja com o intuito de disponibilizar outros clientes (muitas vezes nativos a alguma plataforma, como o Android) ou para expor informações de acordo com os requisitos de outros desenvolvedores. Quando chega esse momento, precisamos de web services para prover acesso a esses dados.

Para entender melhor, exploraremos o exemplo do Facebook. Devido ao seu grande crescimento como rede social, ele encontrou diversas oportunidades que o ajudaram a crescer ainda mais. Entre elas, podemos destacar duas que envolvem a criação de web services: a expansão do mercado mobile, exigindo aplicações nativas para tais dispositivos, e a expansão de suas funcionalidades através de aplicações de terceiros.

Essas oportunidades têm em comum a necessidade de web services para suprir informações para as aplicações externas. Afinal, a web comum é baseada em HTML, que possibilita uma boa interação do sistema com pessoas. Entretanto, quando a aplicação precisa interagir com outras aplicações, essas páginas não são uma boa opção.

Assim como o Facebook, há momentos em que necessitamos desenvolver web services para nossas aplicações. Nesses momentos, duas siglas vêm à nossa mente: SOAP e REST. Muitos vão conhecer melhor SOAP, que significa Simple Object Access Protocol e tenderão a escolhê-lo sobre o REST, que significa Representational State Transfer. Discutir qual é o melhor está fora do escopo deste artigo. Porém, considerando os motivos do Facebook, que também podem ser os seus, justificaremos o porquê da escolha do REST.

Para iniciar essa discussão, vamos primeiro rever os motivos do Facebook. Em resumo, ele desejava prover acesso para aplicações clientes, principalmente mobile, e a outros sistemas de terceiros. Nesse contexto, analisaremos a seguir o que torna o REST a melhor opção:

1. O SOAP é uma especificação de um protocolo estruturado em XML. Devido ao tamanho de suas mensagens, elas naturalmente ocupam mais rede, o que pode ser um problema para aplicações mobile;

2. A complexidade na comunicação com um web service SOAP também é um problema para aplicações mobile e HTML5, visto que são necessárias bibliotecas específicas, utilização de geradores de código e ainda a manutenção do código gerado nas diferentes plataformas;

3. O SOAP foi construído com o pensamento da comunicação entre servidores, em que é assumido que eles são naturalmente seguros. Não se pode garantir que uma aplicação rodando em um dispositivo de um usuário possua tal segurança;

4. O REST é mais simples. Foi projetado para ser usado em clientes “magros”, o que o torna ideal para utilização em dispositivos com capacidades limitadas;

5. As respostas do REST são cacheáveis, diferente do SOAP. Isso dá um grande aumento de performance em clientes simples;

6. Enquanto o SOAP utiliza apenas XML, o REST pode se comunicar através de diversos formatos, sendo JSON o mais usado. Por ser um formato menos verboso e normalmente menor, a transferência de dados com o uso de JSON causa uma carga menor na rede, além de ser mais facilmente consumido por clientes construídos com qualquer linguagem, em especial HTML5.

Como podemos verificar, no contexto apresentado, a melhor opção é o REST. Mas isso não significa que ele é perfeito e fácil de implementar. Consumir um web service bem construído e bem documentado que utiliza REST é fácil e dá grande produtividade para o desenvolvedor.

Porém, o mesmo não se aplica para a sua construção. Veremos que são muitos os detalhes a serem considerados na construção de uma boa RESTful API para outros desenvolvedores.

REST

Diferente do SOAP, que é uma especificação, o REST é algo mais abstrato: um estilo arquitetural. Ele é formado por um conjunto de regras no topo do protocolo HTTP que foram comunicadas pela primeira vez por Roy Fielding em sua tese de doutorado. Depois de seu surgimento, começou a se tornar uma alternativa mais leve de comunicação entre computadores (cliente e servidor).

Para entendê-lo, devemos saber cada uma de suas seis regras. Para ser considerado REST, o web service deve ser ou possuir: Interface uniforme, Stateless, Cacheável, Cliente-Servidor, Sistema em camadas e Código sob demanda. Veremos em detalhes cada uma delas a seguir.

Interface uniforme

Essa regra define que deve haver uma interface uniforme de comunicação entre o cliente e o servidor. O objetivo dela é torná-los independentes um do outro. Isso facilita a criação de uma API, ou seja, um contrato a ser seguido por terceiros na utilização da mesma. Dessa forma, assim como acontece com interfaces no Java, os utilizadores não precisam saber os detalhes da implementação.

Em cima dessa regra, são definidos alguns princípios. O primeiro deles é que o REST deverá ser baseado em recursos identificados por URLs. Diferente de SOAP, que é baseado em ações, como “obter produto”, o REST é baseado no recurso, deixando a definição das ações para os métodos HTTP. Portanto, quando quisermos obter um recurso, como um produto, faremos uma requisição GET para a URL que o localiza.

Assim, é importante entendermos que as URLs serão definidas por substantivos e não por verbos, salvo quando for necessário fazer algum cálculo ou algo que não esteja vinculado a um recurso no servidor. O motivo disso é bem simples: tornar a interface simples. A Listagem 1 mostra exemplos de URLs para um recurso Produto que não seguem esse padrão e a Listagem 2 mostra exemplos que o seguem.

Listagem 1. Exemplos de URL que não seguem os princípios do REST.

 
  /listarProdutos = URL para listar todos os produtos
  /obterProduto/1 = URL para obter o produto 1
  /criarProduto = URL para criar produto
  /deletarProduto = URL para deletar produto

Listagem 2. Exemplos de URL que seguem os princípios do REST.

 
  /produtos = URL para ações sobre recursos do tipo Produto
  /produtos/1 = URL para ações sobre um produto específico

Em ambas as listagens foram mostradas apenas o CRUD básico desse recurso. Porém, a Listagem 2 parece incompleta. Afinal, onde definimos as ações? Elas são definidas utilizando os métodos do protocolo HTTP: GET, POST, PUT, DELETE e HEAD. Veremos mais a frente como cada um desses métodos é usado, mas pode-se adiantar que os quatro primeiros serão traduzidos nas operações de CRUD.

Ainda observando os exemplos da Listagem 2, podemos ter outra dúvida: por que utilizar o plural? Bem, essa resposta é bem direta: simplicidade novamente. Utilizando o plural, as operações de leitura para uma lista ficam naturais, mas parece menos natural quando nos referimos a uma única instância do recurso. No entanto, por simplicidade, mantemos todas as URLs de determinado recurso com a mesma raiz (parte inicial da URL), que por padrão está no plural.

Outra definição importante é que não obteremos recursos através dessas chamadas ao servidor, mas sim suas representações. Por isso a sigla REST significa Representational State Transfer. Transferimos do servidor para o cliente e vice-versa representações de um recurso, sendo eles representados de diversas formas. Usa-se predominantemente JSON, mas também é comum representá-los no formato XML.

Essa ideia de representações de recursos também pode ser identificada na Web. Nela, o usuário localiza um recurso por uma URL. Esse recurso é mostrado de forma legível para humanos através de uma representação em HTML, a qual é interpretada pelo navegador e o mesmo cria uma visualização para o usuário. Aqui estamos fazendo algo parecido. A diferença é que usamos representações a serem lidas e interpretadas por outras aplicações. Afinal, diferente de seres humanos, elas não precisam da aparência visual, e sim dos dados.

Podemos levar essa comparação adiante introduzindo mais um princípio definido pelo REST: HATEOAS. Essa sigla significa Hypermedia as the Engine of Application State. Esse princípio define que o estado da aplicação, ou seja, o estado dos recursos desta, é transferido entre o cliente e o servidor na forma de hypermedia: hypertexto com hyperlinks.

Isso significa que, dada a requisição por um recurso, tanto o seu link quanto os links dos recursos relacionados serão retornados no corpo da resposta para essa requisição. Assim, aplicações clientes podem ser guiadas a outros recursos através dos hyperlinks fornecidos pelo próprio resultado de uma chamada à API.

Se compararmos isso com a Web que conhecemos, podemos perceber que o HTML que o servidor retorna para nosso navegador também é uma hypermedia. Nela estão links que usamos para encontrar outros recursos relacionados, o que faz com que possamos ser guiados para outros recursos facilmente sem precisarmos necessariamente saber a URL que os localiza.

Para exemplificar isso no contexto de REST, imagine que estamos utilizando a API do Facebook para obter as postagens de um usuário. Como é esperado que tivessem diversos itens como resultado, é natural pensarmos numa paginação dos mesmos. Então, quando fazemos a requisição, podemos ver que o próprio resultado já nos retorna um hyperlink para a próxima página de resultados. Esse é apenas um exemplo real usado para mostrar como as representações dos recursos são interligadas na forma de hypermedia. Isso é feito através da utilização de URLs para identificar os recursos.

O último princípio a ser apresentado dentro dessa regra de Interface Uniforme é o que diz respeito às mensagens serem autodescritivas. Isso significa que as mensagens devem trazer informações sobre o formato de representação a ser utilizado, a possibilidade de fazer cache dos dados, entre outras informações que as tornam completas.

Veremos mais sobre os formatos mais adiante. No momento, continuaremos expondo as regras definidas pelo REST.

Stateless

Essa é uma das regras mais importantes do REST. Ela dita que cada requisição deverá conter todas as informações relevantes para ser processada. Ou seja, não deve haver manutenção de estado entre as requisições. Caso haja alguma manutenção de estado, esse deverá ser mantido pelo cliente e enviado a cada nova requisição.

Assim, a única responsabilidade do servidor é utilizar os dados recebidos na requisição, fazer o processamento necessário e retornar tudo que for pertinente ao cliente.

Isso pode parecer estranho a princípio para quem está acostumado a desenvolver para a Web, em que sempre possuímos um estado guardado no servidor para cada cliente. Porém, essa regra abre caminho para obter uma enorme escalabilidade, visto que o servidor não precisará manter uma sessão e se comunicar utilizando-a.

Com isso, poderíamos criar clusters de servidores espalhados e ter um balanceador de carga sem nos preocuparmos se a requisição do usuário será tratada por um servidor que possui seus dados de sessão.

Veremos mais a frente que essa regra é descumprida em alguns casos, como o caso da three-legged OAuth, que é uma especificação de segurança. Mas essa regra deve ser sempre seguida, a não ser em casos especiais.

Cacheável

O servidor, sempre que possível, deverá indicar ao cliente a possibilidade do mesmo fazer cache dos resultados retornados. Essa capacidade de fazer cache reduz o tráfego de dados entre o cliente e o servidor, melhorando a escalabilidade do servidor e melhorando a performance de ambos. Veremos mais a frente como podemos utilizar uma técnica baseada em ETag para se obter tal capacidade.

Cliente-Servidor

Apoiado na regra da interface uniforme, esta é a regra que separa as responsabilidades do cliente e do servidor. Ela indica que é de responsabilidade do servidor o armazenamento dos dados, o que aumenta a portabilidade do cliente. Também diz que é de responsabilidade exclusiva do cliente a apresentação da interface com o usuário, bem como a manutenção da sessão dele, o que torna os servidores mais escaláveis.

Assim, o cliente e o servidor poderão ser desenvolvidos de maneira independente, enquanto a interface de comunicação entre eles permanecer inalterada.

Sistema em camadas

Essa regra abre possibilidade para a divisão do servidor em um sistema em camadas, visto que ela afirma que a interface da API deve funcionar de tal forma que o cliente não consiga diferenciar se está sendo atendido pelo servidor final, um servidor que utiliza os serviços do final ou mesmo se o servidor atual é o mesmo que atendeu as requisições anteriores.

Assim, a escalabilidade é mais uma vez aumentada, visto que podemos configurar um cluster de servidores conectados por um balanceador de carga, sem que o cliente precise notar isso.

Código sob demanda

A regra que diz que o servidor poderá estender as funcionalidades do cliente através da transferência de lógica a ser executada pelo mesmo, como um script em JavaScript a ser executado pelo cliente, é a única regra opcional. Ela apenas indica a possibilidade do servidor fazer isso, mas não torna tal funcionalidade necessária para definir um serviço como REST.

Com isso, concluímos as seis regras que compõem o estilo arquitetural do REST. Nosso próximo objetivo é entender melhor como as ações sobre os recursos são vinculadas aos métodos HTTP.

HTTP Methods

Como foi discutido anteriormente, os recursos são identificados por URLs com a mesma raiz, que é construída com o nome do recurso no plural. A partir disso, todas as ações sobre esses recursos são feitas utilizando métodos HTTP: GET, POST, PUT, DELETE e HEAD. Isso mostra que, diferente da Web comum que conhecemos, o REST faz um uso mais avançado das capacidades do protocolo HTTP, utilizando os outros métodos não suportados nela, visto que a mesma possui apenas requisições dos tipos GET e POST.

Antes de mapearmos cada um desses métodos para suas respectivas ações sobre um recurso, vamos falar sobre um atributo que alguns deles têm em comum: a idempotência.

Idempotência

Uma ação idempotente é aquela que produzirá o mesmo resultado independente de quantas vezes ela seja executada. Isso significa que quando executarmos uma ação idempotente múltiplas vezes em um recurso, seu estado ficará de tal forma como se tivéssemos executado apenas uma vez.

Esse atributo é importante, pois há momentos em que não temos certeza se nossa requisição chegou ao servidor. Isso pode acontecer, por exemplo, se a internet cair durante a requisição. Não saberemos se o servidor executou nossa requisição e não conseguiu responder devido ao problema de comunicação ou se nossa requisição nem mesmo chegou ao servidor.

Se a ação que estamos tentando executar for idempotente, podemos simplesmente repetir a requisição, visto que esse tipo de ação garante a manutenção do estado do recurso como se uma única requisição tivesse sido enviada.

Esse atributo é muito importante, mas não está presente em todas as ações possíveis sobre um recurso. À medida que explorarmos cada um dos métodos HTTP com suas respectivas ações, indicaremos se o mesmo possui ou não idempotência.

HTTP GET

O método GET é um dos que deixa mais claro, pelo seu próprio nome, qual tipo de operação ele realiza. Naturalmente, esse tipo de requisição é mapeada para a operação de leitura do recurso. O resultado para ela é que uma representação, em determinado formato, do recurso, é retornada pelo servidor no corpo da resposta.

Temos algumas opções nesse tipo de requisição. Podemos requisitar uma única instância de um recurso, utilizando seu ID na URL como visto anteriormente, e também requisitar uma coleção de recursos. Em ambos os casos, podemos dizer ao servidor em qual formato queremos a representação desse recurso, sendo em JSON ou XML, por exemplo, dependendo da disponibilidade desses formatos no servidor.

Também veremos mais a frente que uma boa prática a ser implementada é a possibilidade de paginação da coleção de recursos. Além disso, veremos em breve como selecionar os campos desejados, fazer um filtro na recuperação, entre outras boas práticas.

Ainda sobre a requisição do tipo GET, por ser uma operação de leitura, é naturalmente idempotente, visto que ela nem mesmo altera o estado do recurso. Portanto, caso haja uma falha de conexão, podemos simplesmente repetir a requisição sem se preocupar com qualquer efeito colateral.

Mais detalhes sobre as respostas para essas requisições, incluindo o status HTTP retornado pelo servidor em diferentes situações, serão vistos adiante.

HTTP DELETE

Assim como o GET, o nome do DELETE também é bem intuitivo e deixa bem claro o que ele faz: deleta uma ou mais instâncias de um recurso. Essa requisição pode ser executada para uma instância única utilizando o ID na URL ou para todas as instâncias através da raiz do recurso. Também é possível deletar múltiplos recursos específicos a partir de uma filtragem dos resultados, o que será visto mais a frente.

Porém, por ser uma operação crítica, é comum que o servidor a disponibilize apenas para requisições com o ID a ser deletado.

Sendo essa uma operação que remove totalmente um recurso, ela é idempotente. A única diferença que pode acontecer em múltiplas chamadas é que as chamadas consecutivas serão respondidas pelo servidor com uma mensagem que indique que o recurso não foi encontrado. Naturalmente, com essa resposta, o cliente pode inferir que o recurso foi deletado em uma das requisições anteriores.

HTTP HEAD

O método HEAD é um dos menos comuns e normalmente não é disponibilizado pelo servidor. Seu objetivo é parecido com o GET, com a diferença que ele retornará apenas metadados sobre o recurso requisitado, ao invés de trazer suas representações completas. Essa requisição poderia ser usada para saber informações sobre uma coleção de recursos, como a quantidade total, e também para se obter informações sobre um recurso específico, como a data da última modificação.

Deste modo, como o GET, essa requisição também é idempotente, visto que não causa nenhuma alteração de estado nos recursos.

HTTP POST

O método POST é usado na Web comum para se criar e atualizar um determinado recurso através de um formulário. No contexto de REST, ele é usado de forma similar. POST possui duas funções: criar um recurso (sem especificar o ID do mesmo, de forma a deixar que o servidor o gere) e fazer uma atualização parcial do mesmo.

Por atualização parcial, entendemos como uma atualização em que apenas alguns campos de uma instância de recurso são modificados, deixando os outros sem qualquer modificação.

Quando utilizamos esse método para criar um recurso, deixando que o servidor gerencie seu ID, naturalmente precisamos saber com qual ID ele foi criado. Portanto, o servidor deve responder a essa requisição com a localização do recurso, sendo essa a URL que unicamente o localiza.

Através dessa URL, sabemos a localização e o ID do mesmo, que são informações importantes para continuarmos interagindo com ele. Pode ser, também, que o servidor adicione na resposta a representação desse novo recurso. Isso pode muitas vezes ser inútil, já que o cliente possui a representação que acabou de ser criada no servidor, mas pode fazer sentido em alguns momentos em que tal recurso possua outros campos gerados pelo servidor.

Por sua natureza de criação de um recurso novo ou atualização parcial de um recurso existente, esse método não é idempotente. Se repetirmos a requisição de criação, teremos múltiplos recursos criados com estado duplicado. No caso da requisição de atualização parcial, podemos deixar o recurso inconsistente se outra requisição desse tipo for realizada por outro cliente em paralelo. Portanto, esse método deve ser utilizado com cuidado, devido à sua natureza não idempotente.

HTTP PUT

O método PUT não é utilizado na Web comum, mas podemos imaginar seu significado pelo seu nome. Através desse nome, imaginamos que ele “coloca” algo no servidor. Assim, ele possui duas funções: fazer uma atualização completa de determinado recurso (especificado pela URL) e criar um recurso especificando o ID.

Como é comum que seja de responsabilidade do servidor a criação do ID de seus recursos, sua segunda função é menos comum e pode não ser disponibil" [...]

A exibição deste artigo foi interrompida :(
Este post está disponível para assinantes MVP

 
Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Receba nossas novidades
Ficou com alguma dúvida?