Criando uma API RESTful com a JAX-RS API

No Java, quando pensamos na criação de uma API RESTful a primeira opção que vem a mente é o projeto Spring. Este projeto facilita a criação de APIs REST com o Spring Boot, que é uma das bibliotecas fornecidas por ele. Entretanto, o Java define uma especificação para este tipo de aplicação, a JAX-RS API.

Mesmo não possuindo tanto destaque quanto o Spring Boot, como veremos neste artigo, é bem simples criar uma API REST com a JAX-RS API.

Java - Fundamentos de JAX-WS e JAX-RS
Curso de Java - Fundamentos de JAX-WS e JAX-RS
CONHEÇA O CURSO

A especificação JAX-RS API

A JAX-RS API trata-se de uma especificação, ela define interfaces e anotações fornecidas pelo Java EE, que podem ser utilizadas na criação de uma API RESTful. Um desenvolvedor pode criar a sua aplicação baseada unicamente nesta especificação, entretanto o usual é fazer uso de uma biblioteca que a implemente.

Entre as bibliotecas existentes, as implementações da JAX-RS API mais conhecidas são RESTEasy e Jersey. Sendo que a mais utilizada é a Jersey, assim neste artigo faremos uso dela.

Então vamos colocar a mão na massa.

Criando a aplicação

Para este artigo irei criar um projeto Maven utilizando o arquétipo jersey-quickstart-grizzly2, pois este arquétipo já possui o Jersey configurado e fornece embutido o servidor Grizzly.

Pelo terminal, o projeto pode ser criado com o comando abaixo:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=br.com.treinaweb -DartifactId=jaxrsexample -Dpackage=br.com.treinaweb

No arquivo pom.xml haverão as dependências do Jersey:

<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.inject</groupId>
  <artifactId>jersey-hk2</artifactId>
</dependency>

Caso não queira utilizar o Jersey com o Grizzly, você pode substituir a primeira dependência acima pelas abaixo:

<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-server</artifactId>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-servlet</artifactId>
</dependency>

Com o projeto criado e o Jersey configurado, vamos começar a criar a nossa API REST com JAX-RS API.

Criando uma entidade e repositório

Este exemplo será um CRUD simples, mas para isso necessitamos de uma entidade, que será representada pela classe abaixo:

public class Pessoa {
    private int id;
    private String nome;
    private int idade;

    public int getId() {
        return id;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public void setId(int id) {
        this.id = id;
    }
}

Esta aplicação não lidará com um banco de dados, assim o repositório utilizará um hashmap na memória:

public class PessoaRepository {
    private final static HashMap<Integer, Pessoa> pessoas = new HashMap<>();

    public List<Pessoa> GetAll(){
        return new ArrayList<Pessoa>(pessoas.values());
    }

    public Pessoa Get(final int id) {
        return pessoas.get(id);
    }

    public void Add(final Pessoa pessoa) {
        if(pessoa.getId() == 0 )
            pessoa.setId(generateId(pessoas.size() + 1));
        pessoas.put(pessoa.getId(), pessoa);
    }

    public void Edit(final Pessoa pessoa) {
        pessoas.remove(pessoa.getId());
        pessoas.put(pessoa.getId(), pessoa);
    }

    public void Delete(final int id) {
        pessoas.remove(id);
    }

    private int generateId(final int possible)
    {
        if(pessoas.containsKey(possible))
            return generateId(possible + 1);
        return possible;
    }
}

Agora podemos criar o nosso recurso.

Recursos

No Jersey os endpoints são chamados de recursos. Um recurso nada mais é que uma classe que contém a anotação @Path:

import javax.ws.rs.Path;

@Path("/pessoa")
public class PessoaResource {

}

Como é possível notar no código acima, a anotação @Path é utilizada para indicar um caminho. Ao defini-lo na classe, significa que este será o nosso endpoint. Dentro dela, definimos métodos que representam os verbos HTTP, onde cada método deverá conter a anotação equivalente ao verbo que o invocará:

  • @DELETE = DELETE;
  • @GET = GET;
  • @HEAD = HEAD;
  • @OPTIONS = OPTIONS;
  • @POST = POST;
  • @PUT = PUT;
  • @PATCH = PATCH.

Neste nosso endpoint não utilizaremos todas essas anotações, apenas as equivalentes aos principais verbos: DELETE, GET, POST e PUT.

Ao realizar este procedimento, a classe ficará com o seguinte código:

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import br.com.treinaweb.model.Pessoa;
import br.com.treinaweb.repositories.PessoaRepository;

@Path("/pessoa")
public class PessoaResource {

    private PessoaRepository _repositorio = new PessoaRepository();

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Pessoa> get() {
        return _repositorio.GetAll();
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Pessoa getById(@PathParam("id") int id) {
        return _repositorio.Get(id);
    }

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(Pessoa pessoa)
    {
        try{
            _repositorio.Add(pessoa);
            return Response.status(Response.Status.CREATED).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }

    @PUT
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response put(@PathParam("id") int id, Pessoa pessoa)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            pessoa.setId(id);
            _repositorio.Edit(pessoa);
            return Response.status(Response.Status.OK).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }

    @DELETE
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response delete(@PathParam("id") int id)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            _repositorio.Delete(id);
            return Response.status(Response.Status.OK).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }
}

Repare que os métodos também definem o tipo de conteúdo que irão produzir/retornar:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Pessoa> get() {
  return _repositorio.GetAll();
}

E o tipo que irão consumir:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response post(Pessoa pessoa)
{
  try{
    _repositorio.Add(pessoa);
    return Response.status(Response.Status.CREATED).entity(pessoa).build();
  }
  catch(Exception ex)
  {
    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
  } 
}

O Jersey se encarregará de converter o objeto retornado para JSON e o JSON recebido para o tipo do objeto indicado. Mas para que esta a conversão seja possível, é necessário definir a dependência abaixo (caso não esteja definida):

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-binding</artifactId>
</dependency>

Pronto, a nossa aplicação já pode ser utilizada.

Testando a aplicação

Caso esteja acompanhando este artigo e se o seu projeto foi criado com base o arquetipo jersey-quickstart-grizzly2, você pode compilar os códigos pelo Maven:

mvn clean compile

E executar o servidor embutido da aplicação:

mvn exec:java

Indiferente da forma que a aplicação é executada, poderemos interagir com o nosso endpoint:

POST:

Tela no Postman, mostrando o exemplo de uma requisição POST para o endpoint "http://localhost:8080/pessoa"

GET:

Tela no Postman, mostrando o exemplo de uma requisição GET para o endpoint "http://localhost:8080/pessoa"

PUT:

Tela no Postman, mostrando o exemplo de uma requisição PUT para o endpoint "http://localhost:8080/pessoa/1

GET ID:

Tela no Postman, mostrando o exemplo de uma requisição GET para o endpoint "http://localhost:8080/pessoa/1"

DELETE:

Tela no Postman, mostrando o exemplo de uma requisição DELETE para o endpoint "http://localhost:8080/pessoa/1"

AngularJS - Criação de interfaces web
Curso de AngularJS - Criação de interfaces web
CONHEÇA O CURSO

Conclusão

A JAX-RS API é uma poderosa especificação que pode ser aplicada em uma aplicação facilmente graças a implementação do Jersey, se tornando uma ótima alternativa para o Spring Boot.

Desta forma, caso necessite criar uma aplicação RESTfull API no Java, não deixe de dar uma olhada nos recursos fornecidos pela JAX-RS API e o Jersey.

Você pode baixar a aplicação deste artigo no meu Github.

Deixe seu comentário

Instrutor, nerd, cinéfilo e desenvolvedor nas horas vagas. Graduado em Ciências da Computação pela Universidade Metodista de São Paulo.

© 2004 - 2019 TreinaWeb Tecnologia LTDA - CNPJ: 06.156.637/0001-58 Av. Paulista, 1765, Conj 71 e 72 - Bela Vista - São Paulo - SP - 01311-200