Capítulo 11

REST

11.1 - O que é REST

REST é um conjunto de restrições que define um padrão arquitetural com características específicas: benefícios e dificuldades determinadas aparecerão ao implementar um sistema seguindo o padrão REST.

11.2 - Características e vantagens

Permitir o endereçamento dos recursos do seu sistema de uma forma padronizada implica em algumas vantagens do REST: as URIs são bookmarkable, o conteúdo é mais suscetível a cache e os componentes agindo entre o cliente e o servidor possuem uma visão melhor do que está sendo enviado de um lado para o outro permitindo que os mesmos tomem decisões específicas para aumentar a escalabilidade ou a segurança do seu sistema.

Utilizar o protocolo HTTP não somente como uma maneira de transmitir dados mas também como um protocolo de aplicação permite maior visibilidade para os componentes intermediários.

Conteúdo hipermídia permite a navegabilidade de um cliente através do seu sistema, além de diminuir o acoplamento entre o cliente e o servidor, que costuma ser muito alto em aplicações orientadas a serviço.

O tópico de hipermídia é bem amplo e não será abordado no curso, sugerimos a visita ao site do restfulie-java para entender mais como ele funciona e suas vantagens em um sistema: http://github.com/caelum/restfulie-java

Saber inglês é muito importante em TI

O Galandra auxilia a prática de inglês através de flash cards e spaced repetition learning. Conheça e aproveite os preços especiais.

Pratique seu inglês no Galandra.

11.3 - O triângulo do REST

Uma requisição web tem três tipos de componentes importantes: substantivos, verbos e tipos de conteúdo (Content Types). Uma aplicação RESTful costuma reforçar mais os substantivos do que os verbos, além de conter uma gama de tipos de conteúdo determinados. A vantagem de seguir isso é que conseguimos aproveitar toda a estrutura que o protocolo HTTP proporciona.

Substantivos: Recursos

Substantivos são os nomes dos recursos do seu sistema. Quando fazemos requisições Web, precisamos falar o caminho da mesma, a URI (Unified Resource Identifier), ou seja, o identificador único de um recurso.

Então ao criar as URIs do nosso sistema devemos levar em conta que elas representam recursos, não ações. Em sistemas REST, nossas URIs devem conter apenas substantivos, que são nossos recursos: /produtos/adiciona não é boa, pois contém um verbo e não está identificando um recurso, mas sim uma operação. Para representar a adição de um produtos podemos usar a URI /produtos com o método HTTP POST, que representa que estamos adicionando alguma informação no sistema.

Verbos: Operações

Uma das característas mais importantes de REST é que você tenha um conjunto pequeno e fixo de operações bem definidas, gerando uma interface uniforme. Desse modo, para duas aplicações conversarem elas não precisam implementar diversas operações: basta usar as operações definidas. Perceba que adicionar um produto e adicionar um usuário no sistema são operações equivalentes.

O protocolo HTTP possui sete operações -- os métodos GET, POST, PUT, DELETE, HEAD, OPTIONS e TRACE. Duas delas (GET e POST) já são bem conhecidas e utilizadas: GET quando clicamos em links ou digitamos o endereço no navegador, e POST quando preenchemos formulários de cadastro. Cada método tem uma semântica diferente e juntando o método à URI deveríamos conseguir representar todas as ações do nosso sistema. As semânticas principais são:

Content Type: Representação

Quando fazemos uma aplicação não trafegamos um recurso pela rede, apenas uma representação dele. Por exemplo, quando queremos adicionar um produto ao sistema, passamos para o servidor uma representação do produto: dados do formulário. E quando pedimos uma lista de produtos para o servidor ele responde com uma representação HTML desses produtos. Mas não precisa ser restrito a isso: poderíamos adicionar um produto ao sistema via uma representação em XML, e o servidor poderia nos devolver uma lista de produtos em formato JSON.

Resumindo: nossas URIs devem representar recursos, as operações no recurso devem ser indicadas pelos métodos HTTP e podemos falar qual é o formato em que conversamos com o servidor com o Content Type. Nas próximas seções vamos ver como aplicar essas idéias usando o VRaptor.

11.4 - Mudando a URI da sua lógica: @Path

Como vimos anteriormente, o VRaptor possui uma convenção para gerar as URIs das suas lógicas. Mas, se você está fazendo seu sistema seguindo REST, essa convenção não é muito boa: métodos geralmente contêm verbos, e não é bom colocar verbos nas URIs. Então precisamos sobrescrever essa convenção do VRaptor, e podemos fazer isso facilmente usando a anotação @Path. Por exemplo, se quisermos que o método lista do ProdutosController responda à URI /produtos, basta anotá-lo:

import br.com.caelum.vraptor.Path;

@Resource
public class ProdutosController {
  
  @Path("/produtos")
  public List<Produto> lista() {
    return dao.listaTudo();
  }
}

Dessa forma, o método lista não vai mais estar acessível pela URI /produto/lista, ele apenas poderá ser acessado pela URI /produtos.

Com a anotação @Path ainda é possível usar templates para extrair parâmetros a partir da URI. Por exemplo, no nosso método edita, precisamos passar um query parameter para falar qual é o produto que queremos editar:

/produto/edita?id=10

As URIs devem identificar recursos (além de não conter verbos, mas vamos tirá-los em breve), e o id é algo que identifica o recurso que queremos acessar, então ficaria melhor se passássemos esse id dentro da URI, como por exemplo:

/produto/10/edita

ou seja, uma URI que representa a edição do produto de id 10. Para extrair esse 10 da URI, podemos colocar um template no nosso @Path. Para isso basta colocar o nome do parâmetro que queremos extrair entre chaves, dentro da URI do @Path, e então receber o parâmetro como argumento do método:

@Path("/produto/{id}/edita")
public Produto edita(Long id) {
  return dao.carrega(id);
}

Assim é possível criar URIs mais representativas, que identificam verdadeiramente recursos específicos.

11.5 - Mudando o verbo HTTP dos seus métodos

Podemos também mudar o(s) verbo(s) HTTP que os métodos dos nossos controllers respondem. Por padrão, você pode acessar seus métodos usando qualquer um dos verbos HTTP. Mas, como vimos, cada verbo HTTP tem uma semântica diferente, e devemos levar em conta essas semânticas quando acessamos nossas lógicas. Se uma operação não segue a semântica de algum dos verbos, não deve aceitar o mesmo.

Por exemplo, nosso método lista é uma operação idempotente: não tem nenhum efeito colateral, posso chamá-lo várias vezes e, se o banco de dados não mudou, o resultado vai ser o mesmo. Logo faz sentido que o verbo HTTP para acessar esse método seja o GET. Para fazer essa restrição, basta anotar o método com @Get:

import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Get;

@Resource
public class ProdutosController {
  
  @Path("/produtos")
  @Get
  public List<Produto> lista() {
    return dao.listaTudo();
  }
}

ou ainda o atalho:

import br.com.caelum.vraptor.Get;

@Resource
public class ProdutosController {
  
  @Get("/produtos")
  public List<Produto> lista() {
    return dao.listaTudo();
  }
}

Se quisermos permitir outros verbos HTTP, podemos usar as anotações @Post, @Put, @Delete, @Trace e @Head. A partir do momento que colocamos uma anotação de verbo HTTP no nosso método, ele não poderá ser acessado pelos outros verbos. Então se tentarmos fazer uma requisição POST /produtos, ela não vai cair no método lista, a requisição vai retornar um status 405 (Método não suportado), que quer dizer que existe um recurso nessa URI, mas ele não suporta o verbo HTTP da requisição.

Assim, podemos colocar mais de um método que responda à mesma URI, desde que os verbos HTTP sejam diferentes. Por exemplo, podemos fazer com que o método adiciona também responda à URI /produtos. Mas o método adiciona não é idempotente: se eu repetir duas vezes uma requisição a esse método, vou adicionar dois produtos ao sistema. Estamos adicionando produtos, mas não sabemos qual vai ser a URI dele, então podemos colocar o verbo POST.

import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Post;

@Resource
public class ProdutosController {

  @Post("/produtos")
  public void adiciona(Produto produto) {
    //...
  }  
  
  @Get("/produtos")
  public List<Produto> lista() {
    return dao.listaTudo();
  }
}

Você não está nessa página a toa

Você chegou aqui porque a Caelum é referência nacional em cursos de Java, Ruby, Agile, Mobile, Web e .NET.
Faça curso com quem escreveu essa apostila.

Consulte as vantagens do curso Web ágil com VRaptor, Hibernate e AJAX.

11.6 - Refatorando o ProdutosController

O nosso ProdutosController é a classe responsável por controlar um recurso do sistema: o Produto. Nele podemos realizar várias operações em cima dos Produtos, então podemos padronizar as URIs que vão executar tais operações. Vamos remover todos os verbos das URIs, e usar o verbo HTTP para determinar qual será a operação, assim podemos criar URIs que representam recursos.

A URI /produtos representa a lista de todos os produtos do sistema. E as operações possíveis são:

GET /produtos => recupera a lista de todos os 
          produtos. Método lista.
POST /produtos => adiciona um produto na lista de todos os 
          produtos. Método adiciona.

As URIs do tipo /produtos/42 representam um produto específico, no caso de id 42. As operações possíveis são:

GET /produtos/4 => mostra o produto de id 4. Método edita.
PUT /produtos/10 => atualiza o produto de id 10. Método atualiza.
DELETE /produtos/3 => remove o produto de id 3. Método remove.

Nem todas as URIs precisam representar recursos fixos, mas elas precisam semanticamente representar o mesmo tipo de recurso. Por exemplo a URI /produtos/ultimo pode representar o último produto adicionado e a URI /produtos/maisVendidos pode representar uma lista dos produtos mais vendidos, que são recursos bem definidos. No nosso caso precisamos de uma URI para identificar o formulário de adição de um produto. Vamos então usar a seguinte:

GET /produtos/novo => mostra o formulário para adicionar um novo 
            produto. Método formulario.

Com essa especificação de operações no recurso Produto, podemos usar as anotações vistas anteriormente para deixar o ProdutosController de acordo com ela:

@Resource
public class ProdutosController {

  @Get("/produtos/novo")
  public void formulario() {...}

  @Get("/produtos/{id}")
  public Produto edita(Long id) {...}

  @Put("/produtos/{produto.id}")
  public void altera(Produto produto) {...}

  @Post("/produtos")
  public void adiciona(final Produto produto) {...}

  @Delete("/produtos/{id}")
  public void remove(Long id) {...}

  @Get("/produtos")
  public List<Produto> lista() {...}

}

Note no método altera que o parâmetro extraído é o produto.id. O VRaptor vai se comportar da mesma forma que se esse produto.id tivesse vindo da requisição: vai popular o campo id do produto com o valor extraído.

Agora que mudamos as URIs dos métodos, precisamos atualizar as nossas jsps para usar as URIs novas. Podemos usar a tag c:url para poder usar as URIs absolutas, a partir do nosso nome de contexto.

Primeiro na jsp do formulário, precisamos mudar a action do form para /produtos, e o method para POST.

<form action="<c:url value="/produtos"/>" method="POST">

Na listagem de produtos, temos um link para a edição, vamos mudá-lo:

<a href="<c:url value="/produtos/${produto.id}"/>">Editar</a>

Ainda existe o link de Remoção, mas o método remove do nosso controller só aceita o verbo DELETE! Como fazer uma requisição com o verbo DELETE de dentro da sua página? Infelizmente os browsers atuais só conseguem fazer requisições GET (atráves de links e formulários) e POST(através de formulários). Para conseguir usar os outros verbos, podemos fazer duas coisas:

Então, para criar o link de remoção, precisamos criar um formulário (não podemos usar um <a href> pois isso geraria uma requisição GET que não pode ter efeitos colaterais), que passa o parâmetro _method=DELETE. Podemos ainda fazer com que o botão do formulário pareça um link, para que a página de listagem pareça a mesma de antes:

<!-- Esse pedaço de css já está adicionado no projeto base -->
<style type="text/css">
.link {
  text-decoration: underline;
  border: none;
  background: none;
  color: blue;
  cursor: pointer;
}
</style>

<!-- ... -->

<td>
  <form action="<c:url value="/produtos/${produto.id}"/>" method="POST">
    <button class="link" name="_method" value="DELETE">Remover</button>
  </form>
</td>

Por último precisamos mudar o formulário de edição, colocando a action correta, e mudando o método para PUT. Mas como colocar method="PUT" no nosso formulário não funciona, precisamos passar o parâmetro _method:

<form action="<c:url value="/produtos/${produto.id }"/>" method="POST">
  <fieldset>
      <legend>Editar Produto</legend>
      
      <label for="nome">Nome:</label>
      <input id="nome" type="text" name="produto.nome" 
        value="${produto.nome }"/>
      
      <label for="descricao">Descrição:</label>
      <textarea id="descricao" name="produto.descricao"> ${produto.descricao } </textarea>
      
      <label for="preco">Preço:</label>
      <input id="preco" type="text" name="produto.preco" 
        value="${produto.preco }"/>
      <!--                        vvvvvvv         vvv -->
      <button type="submit" name="_method" value="PUT">
        Enviar
      </button>
  </fieldset>
</form>

Não precisamos mais passar o input hidden com produto.id, porque essa informação já está na URI!

button e o Internet Explorer

Em algumas versões do Internet Explorer, não é possível usar o name e value do button para mandar parâmetros na requisição. Nesse caso você precisa trocar o button por:

<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="Enviar"/>

11.7 - Exercícios

  1. Anote os métodos do ProdutosController para seguirmos a nossa especificação de Produto:

    Recurso Produto:
    GET /produtos => recupera a lista de todos os produtos. 
              Método lista.
    POST /produtos => adiciona um novo produto. 
              Método adiciona.
    GET /produtos/4 => mostra o produto de id 4. 
              Método edita.
    PUT /produtos/10 => atualiza o produto de id 10. 
              Método atualiza.
    DELETE /produtos/3 => remove o produto de id 3. 
              Método remove.
    GET /produtos/novo => mostra o formulário para adicionar um 
              novo produto. Método formulario.
    
  2. Abra o formulario.jsp e mude a action do form:

    <form action="<c:url value="/produtos"/>" method="POST">
    
  3. Abra o edita.jsp e mude a action e o método do form. Lembre-se que você não precisa mais do input hidden do produto.id.

    <form action="<c:url value="/produtos/${produto.id }"/>" 
      method="POST">
      <!-- ... -->
      <button type="submit" name="_method" value="PUT">
        Enviar
      </button>
    </form>
    
  4. Modifique o lista.jsp, colocando o link para Edição e o "link" para Remoção:

    <td><a href="<c:url value="/produtos/${produto.id}"/>">
      Editar
    </a></td>
    <td>
      <form action="<c:url value="/produtos/${produto.id}"/>"
        method="POST">
        <button class="link" name="_method" value="DELETE">
          Remover
        </button>
      </form>
    </td>
    
  5. Abra o header.jspf e modifique os links do menu:

    <div id="menu">
      <ul>
        <li><a href="<c:url value="/produtos/novo"/>">
          Novo Produto
        </a></li>
        <li><a href="<c:url value="/produtos"/>">
          Lista Produtos
        </a></li>
      </ul>
    </div>