Concrete Logo
Hamburger button

Sobre REST e Java

  • Blog
  • 2 de Março de 2012
Share

[fblike]

 

Um pouco de REST

 

REST é um termo usado para descrever um paradigma arquitetural que vem ganhando espaço no terreno de aplicações distribuídas. É usado cada vez mais como alternativa ao (já antigo) SOAP, sendo que a principal crítica a este é a burocracia, algo que REST possui em escala muito menor. É baseado no design do protocolo HTTP, que já possui diversos mecanismos embutidos para representar recursos como códigos de status, representações de tipos de conteúdo, cabeçalhos, etc.

A Web é o principal caso de uso da arquitetura REST. Através destes códigos de status, headers e tudo o mais, é possível que você leia este blog. Por baixo dos panos uma ampla gama de códigos e conteúdo está sendo trocada. Quando você solicita esta página, um servidor lê a sua requisição. Retorna um código de status 200, a representação de ‘OK’, junto com o tipo de conteúdo, no caso HTML e a página em si. Através deste conjunto de informações, o browser interpreta a página como sendo HTML e a renderiza. Quanto às imagens, o que é sabido pelo browser é que a página HTML recuperada possui links para elas. Então faz novas requisições ao servidor. Dessa vez, o tipo de conteúdo pode variar de acordo com o tipo de imagem. O browser determina qual o tipo de imagem e a renderiza adequadamente.

Parecia funcionar muito bem. Roy Fielding, um dos principais autores da especificação do protocolo HTTP, descreveu em 2000 na sua tese de doutorado, o funcionamento daquilo que batizou como REST – mecânica utilizada pelos browsers antes que a técnica tivesse sequer um nome. A idéia de Roy era que isso não fosse utilizado apenas por browsers, mas por vários tipos de cliente incluindo aplicações daquelas que a gente usa em casa. O paradigma passou a ser adotado por algumas empresas. Entretanto várias dessas empresas o adotaram erroneamente. Elas persistiam usando o paradigma RPC (ou seja, a tradicional invocação de método / procedimento remoto) e isto não condizia com o pensamento de Roy.

Leonard Richardson e Sam Ruby publicaram em 2007 o livro Restful Web services. Não é apenas mais um texto técnico sobre REST, mas um guia de boas práticas de uso. Ali nasceu o modelo de adoção de REST baseado em níveis:

    – Nível 0: Significa nada. Zero. Neste nível, não se tem REST, mas apenas o uso de HTTP como protocolo de transferência de dados.

    – Nível 1: Recursos são utilizados. Isso quer dizer que a aplicação possui URI’s bem definidas para representar cada tipo de recurso / entidade da aplicação. Por exemplo, a lista de pessoas cadastradas na aplicação pode ser obtida a partir de uma requisição para /pessoa e a pessoa com ID 1, /pessoa/1.

    – Nível 2: Verbos HTTP passam a ser bem definidos e utilizados. Lembrando que a lista de verbos disponíveis em HTTP são GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT e PATCH. Destes, os mais utilizados por aplicações REST são os quatro primeiros, por aparentarem ligações diretas com as operações básicas de banco de dados (respectivamente, SELECT, INSERT, UPDATE e DELETE).

    – Nível 3: Hipermídia é utilizada para definir o estado da aplicação (abreviada como HATEOAS). Esta é uma capacidade largamente discutida pela web afora, segundo a opinião deste autor, por ser um dos temas que Roy abordou com menos ênfase em sua tese – segundo ele mesmo (leia os comentários), mais por falta de tempo do que por importância. No entanto, basta pensar na maneira como os browsers recuperam as imagens deste blog para saber o que isso significa: os próprios recursos recuperados dão “pistas” de quais URI’s chamar para recuperar recursos relacionados. A maneira como isso vai ser feita (ou seja, qual verbo utilizar, qual tipo de mídia tratar, etc.) é baseada no protocolo geral HTTP (ou seja, seguindo as orientações dos níveis anteriores da escala).

Apesar da escala aparentemente bem definida, as aplicações mundo web afora ainda carecem de bons exemplos de serviços REST. Muitas delas utilizam de maneira plena até o nível 1, de maneira parcial o nível 2 e não adotam o terceiro nível, gerando problemas em diversas implementações .

Aqui neste blog REST já tinha aparecido antes na palestra feita no QCon SP 2011. O propósito deste post é demonstrar como seguir boas práticas em implementações de REST em Java utilizando Jersey, Spring e JPA (Hibernate). Todo o código apresentado aqui está disponível em https://github.com/alesaudate/kickstart-springjerseyhibernate. Sintam-se livres para dar fork e fazer alterações à vontade 😉

 

Mantendo o Norte

O Norte de qualquer desenvolvedor Java deve ser manter qualquer implementação de qualquer sistema o mais simples possível (ou à medida do possível). Pensando nisso, o exemplo criado disponibiliza algumas classes/interfaces que funcionam como base para a criação de recursos gerenciados: BaseService, HATEOASEntity, JpaDao e BaseEntity, que contém alguns dos atributos padrão para persistência em banco de dados. Segue abaixo uma descrição detalhada das classes:

    BaseService: Possui uma implementação padrão de como um DataService REST deve ser. Por exemplo, a criação de um recurso deveria retornar um código HTTP 201 (Created) e um header Location, contendo a URI do recurso recém-criado. A classe BaseService disponibiliza esse tipo de implementação, além de já conter as devidas configurações de segurança – no caso, para métodos de leitura, o cliente precisa ter papel de usuário; para escrita, papel de administrador.

    HATEOASEntity: é uma interface que provê métodos para criação de links – e consequente adoção de HATEOAS.

    BaseEntity: Contém uma implementação default de uma entidade persistente, com todos os atributos normalmente presente em aplicações de negócio, como data de criação, data de atualização, ativo e, é claro, o ID. É diretamente relacionado com a implementação do JpaDao.

    JpaDao: Como diz o próprio nome, é um DAO JPA Genérico, que contém implementações básicas das quatro operações básicas de banco de dados, sendo o SELECT feito com e sem paginação.

Usando estas implementações básicas, está contido no projeto um pacote chamado com.alesaudate.samples.springjersey.example, que contém uma implementação básica do gerenciamento de uma entidade Person. Esta entidade possui como atributos name com tipo String e um Portrait que é uma entidade relacionada. Além disso, o pacote também contém uma classe People (para armazenar coleções de Person), um DAO especializado em Person e a classe Portrait, sendo que esta última contém como atributos os dados do retrato, o tipo MIME e uma descrição, além dos atributos contidos em BaseEntity estendida por esta.

A classe que representa o serviço REST possui o seguinte código:

[sourcecode language=”java” gutter=”false”]
package com.alesaudate.samples.springjersey.example;

//Imports omitidos

@Path("/person")
@Service
public class PersonResource extends BaseService<Person, People> {

public PersonResource() {
super(People.class);
}

public PersonDAO getPersonDAO() {
return (PersonDAO)getDao();
}

@Autowired
public void setPersonDAO(PersonDAO dao) {
setDao(dao);
}

@PUT
@Path("/{id}/portrait")
@Consumes("image/*")
public Response addPortrait(@PathParam("id") Long id, @QueryParam("description") String description, byte[] data, @Context HttpServletRequest request) {
Person person = getPersonDAO().retrieve(id);
if (person == null) {
return Response.status(Status.NOT_FOUND).build();
}
Portrait portrait = new Portrait();
portrait.setData(data);
portrait.setDescription(description);
portrait.setMimeType(request.getContentType());
person.setPortrait(portrait);
getPersonDAO().update(person);
return Response.ok().build();
}

@GET
@Path("{id}/portrait")
public Response getPortrait(@PathParam("id") Long id) {
Person person = getPersonDAO().retrieve(id);
if (person == null || person.getPortrait() == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(person.getPortrait().getData()).type(person.getPortrait().getMimeType()).build();
}

}
[/sourcecode]

Aqui o importante é destacar todo o poder do JAX-RS envolvido. Para realizar a criação da imagem, é necessário realizar o upload referenciando um Person já existente para que seja possível relacionar diretamente o Portrait com o Person já criado. A partir do Content-Type declarado na requisição, o motor JAX-RS (no caso, Jersey) checa se o que está sendo passado é mesmo uma imagem (de qualquer tipo) e caso positivo, executa o método – que por sua vez, cria o portrait a partir dos dados passados no corpo da requisição e os relaciona ao Person recuperado do banco. Na sequência, quando uma requisição GET é feita na tentativa de localizar o retrato, é ajustado na resposta o mime-type passado na requisição de criação/atualização, bem como os dados são retornados no corpo. Isso faz com que a imagem seja vista até por uma requisição efetuada via browser.

 

Testando tudo

Até aqui nenhum recurso específico do Jersey foi utilizado. Todas as anotações, classes, etc., são parte da própria especificação JAX-RS, aumentando a portabilidade da solução. Infelizmente a especificação não provê qualquer espécie de cliente para os serviços. Isto nos obriga escrever os códigos de clientes utilizando API’s nativas – nesse caso a do Jersey. A classe que testa o acesso aos recursos é a PersonClientTest. Ela pode ser usada em modo stand-alone (inicia e para os recursos automaticamente), além de conter um método main() que quando invocado, cadastra um recurso na aplicação (previamente inicializada) para que se possa checar os dados.

Para realizar os testes, eu usei um Tomcat (inicializado na porta 8080). Inicializando a aplicação no Tomcat e executando o método main citado, basta apontar o browser para https://localhost:8080/springjerseyhibernate/person/ (tomando o cuidado de fazer login, usando o usuário admin com senha admin) e verificar o resultado:

[sourcecode language=”xml” gutter=”false”]
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<people xmlns:ns2="https://www.w3.org/1999/xlink">
<person>
<active>true</active>
<creationDate>2012-02-17T19:48:19.537-02:00</creationDate>
<id>1</id>
<updateDate>2012-02-17T19:48:20.439-02:00</updateDate>
<ns2:link rel="portrait" href="1/portrait"/>
<ns2:link rel="self" href="1"/>
<name>Smurf Daddy</name>
</person>
</people>
[/sourcecode]

Note que uma requisição GET em https://localhost:8080/springjerseyhibernate/person/1 retorna apenas o person com o ID 1 e a requisição em https://localhost:8080/springjerseyhibernate/person/1/portrait retorna o retrato da pessoa em questão – assim como os links retornados descrevem.

 

Conclusão

REST quando aproveitado da maneira correta é um mecanismo mais poderoso do que podemos imaginar. A questão de HATEOAS não é apenas “a cereja do bolo”, mas um mecanismo que pode levar à construção de serviços dinâmicos, em que a descoberta do que pode ser feito com o recurso está descrito no próprio recurso. Isso pode nos levar a um dia, ter o conhecimento necessário para construir aplicações em nossas empresas que serão tão flexíveis e extensíveis quanto a própria Web.