Vous êtes sur la page 1sur 69

Desenvolvimento de Web APIs com Java

Ivan Salvadori
Esse livro est venda em http://leanpub.com/javawebapis
Essa verso foi publicada em 2016-03-26

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
2016 Ivan Salvadori

Contedo
Sobre Este Livro .
Pblico Alvo .
Pr-requisitos
Recursos . . .
Sobre o Autor

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

i
i
i
i
i

Introduo . . . . . . . . . . . . .
Web APIs . . . . . . . . . . . .
Princpios Arquiteturais REST
Jersey . . . . . . . . . . . . . .
Spring Boot . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1
1
3
4
6

Projeto . . . . . . . . . . . . . . . . . . .
Viso Geral . . . . . . . . . . . . . . .
Modelagem do Domnio da Aplicao
Integrao de Dados . . . . . . . . . .
Ferramentas Utilizadas . . . . . . . .
Configurao Inicial . . . . . . . . . .
Contratos . . . . . . . . . . . . . . . .
Configurao do Banco de Dados . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

9
9
9
10
11
11
15
16

Implementao das Funcionalidades


Cadastramento . . . . . . . . . . .
Consulta a Todos os Contatos . . .
Consulta a um Contato Especfico
Alterao e Remoo . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

18
18
28
31
35

Tratamento de Excees . . . . . .
Criao de Excees de Negcio
Implementao de Providers . .
Contato No Encontrado . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

41
41
44
45

Aplicao Cliente da Web API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Viso Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Listagem dos Contatos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48
48
49

.
.
.
.

CONTEDO

O Problema de Cross Origin Request


Cadastro . . . . . . . . . . . . . . .
Consulta . . . . . . . . . . . . . . .
Alterao . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

52
54
57
59

Construo e Implantao do Projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

63

Prximos Passos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

64

Sobre Este Livro


Pblico Alvo
Este livro destinado a estudantes de programao interessados em desenvolver sistemas para a Web.
Este livro tambm pode ser interessante para programadores experientes que buscam atualizar seus
conhecimentos sobre o desenvolvimento de Web APIs utilizando a linguagem de programao Java.

Pr-requisitos
Para acompanhar adequadamente os assuntos abordados neste livro, recomenda-se que o leitor
possua conhecimentos bsicos de programao na linguagem Java, de gerenciamento de banco de
dados, de modelo cliente/servidor, alm de conhecer o protocolo HTTP.

Recursos
O cdigo fonte dos projetos est diponvel em https://bitbucket.org/salvadori/livro-java-web-apis.

Sobre o Autor
Ivan Salvadori bacharel (2009), mestre (2015) e doutorando em cincia da computao pela Universidade Federal de Santa Catarina. membro do Laboratrio de Pesquisas em Sistemas Distribudos
(LAPESD-UFSC). Atua na rea de sistemas distribudos, com foco em Web services semnticos.
Atualmente est pesquisando mecanismos de composio para arquitetura de Microservices.

http://lapesd.inf.ufsc.br/

Introduo
Web APIs
Organizaes necessitam interligar sistemas e trocar informaes internamente e tambm com
outras organizaes. Uma soluo simples e muito utilizada para este tipo de integrao atravs
do compartilhamento de banco de dados, onde tabelas so criadas para armazenar e compartilhar os
dados dos sistemas. Esta forma de integrao relativamente simples e rpida de ser implementada,
porm apresenta algumas desvantagens. Com a evoluo dos sistemas, inevitvel que ocorram
alteraes (estruturais ou de contedo) nas bases de dados. Como diversas aplicaes utilizam tabelas
em comum, uma alterao pontual no banco de dados pode afetar diversas aplicaes, dificultando
a evoluo e manuteno dos sistemas integrados.

Integrao de aplicaes atravs de banco de dados

Outra alternativa realizar a integrao atravs de Web APIs, que disponibilizam as funcionalidades
das aplicaes em rede local ou na Web. A principal diferena entre Web APIs e aplicaes
Web tradicionais o usurio. Aplicaes Web tradicionais so manipuladas diretamente por seres
humanos, enquanto Web APIs so projetadas para operar com outros sistemas. No cenrio de
integrao atravs de Web APIs, cada aplicao possui sua prpria base de dados, sem compartilh-la
com os demais sistemas. As informaes so expostas atravs de Web APIs, que formam uma camada
de integrao. Os dados so geralmente representados nos formatos JSON ou XML, e transportados
via HTTP. Com esta abordagem de integrao, a Web se torna uma infraestrutura para construo
de sistemas distribudos.
A integrao por Web APIs possui a vantagem de reduzir o acoplamento entre as aplicaes,
possibilitando que evoluam em ritmos diferentes, pois alteraes nos modelos de dados no
influenciam diretamente a integrao. Outro ponto positivo a possibilidade da integrao se
estender para fora dos domnios da organizao. Como a base de dados no compartilhada, apenas
dados especficos so disponibilizados, atuando como backend para outras aplicaes. Por outro
lado, acrescenta a complexidade de implementao de uma camada extra, responsvel por realizar e
atender chamadas a outras Web APIs, alm converter os dados nos formatos estipulados. Alm disso,
1

Introduo

Web APIs so fundamentais para aplicaes mobile, que geralmente utilizam o suporte server-side
para atender aos seus objetivos.

Integrao de aplicaes atravs de Web APIs

Na integrao atravs de banco de dados, as aplicaes executam operaes de consulta, criao,


alterao e remoo de dados, conhecidas como operaes CRUD (Create, Read, Update, Delete).
esperado que a integrao atravs de Web APIs seja capaz de realizar as mesmas operaes. Na
realidade, grande parte das Web APIs desenvolvidas possuem esta caracterstica. Aplicaes dessa
natureza parecem se encaixar adequadamente ao estilo arquitetural REST. Sendo assim, aplicaes
CRUD que disponibilizam suas funcionalidades atravs de uma Web API podem ser facilmente
integradas com outras aplicaes.

Web APIs como backend para outras aplicaes

Introduo

Princpios Arquiteturais REST


REST (REpresentational State Transfer) uma coleo de princpios e restries arquiteturais
para o desenvolvimento de aplicaes distribudas na Web. REST uma abordagem leve para o
desenvolvimento de Web Services, que busca simplicidade e baixo acoplamento. Recursos formam
a base dos princpios REST. Um recurso agrupa um conjunto de dados que juntos representam uma
unidade de informao coesa. Recursos so acessveis a clientes remotos atravs de representaes,
que so endereadas atravs de um identificador nico, denominado URI (Uniform Resource
Identifier). A representao de um recurso uma amostra dos valores de suas propriedades em
um determinado momento do tempo.
JSON um dos formatos mais utilizados em Web APIs para representar a estrutura e os dados dos
recursos. JSON utiliza uma coleo de pares de chave/valor, onde a chave sempre descrita como
texto, e o valor pode ser expresso como literal, numrico, booleano, nulo, objeto ou uma sequncia
ordenada de valores. muito utilizado no intercmbio de informaes, pois independente de
linguagem de programao e fcil criao, manipulao e anlise.
Dentre os princpios arquiteturais REST est o estabelecimento de uma interface uniforme entre
cliente e servidor. Uma das formas para estabelecer uma interface uniforme respeitar a semntica
do protocolo utilizado pela Web API. O HTTP o protocolo mais utilizados em Web APIs REST, e
respeitar a semntica do protocolo significa utilizar adequadamente os seus verbos. Os verbos HTTP
mais utilizados so:

GET - Obter a representao de um recurso;


POST - Criar um novo recurso;
PUT - Alterar um recurso;
DELETE - Remover um recurso.

Espera-se que o significado dos verbos HTTP sejam respeitados, empregando o verbo adequado para
cada ao, embora muitas implementaes REST negligenciem esta restrio e utilizam GET para
obter, criar, alterar e remover recursos, dentre outras combinaes. Outra restrio imposta pelo
REST a correta utilizao de cdigos de status ou mensagens. Todas as requisies tratadas pelo
servidor recebem um cdigo de status, que informa ao cliente o resultado da requisio. Os cdigos
possuem tamanho fixo de trs dgitos e esto organizados da seguinte forma:

1XX - Informaes;
2XX - Sucessos;
3XX - Redirecionamentos;
4XX - Erros causados pelo cliente;
5XX - Erros causados no servidor.

Introduo

Outra restrio arquitetural REST exige que as requisies contenham todas as informaes
necessrias para sua execuo, sem recorrer a dados armazenados em sesses do usurio, ou seja,
requisies auto-descritivas. No esperado que o servidor mantenha dados na sesso do usurio,
tornando a aplicao stateless, ou seja, o servidor no deve manter nenhuma informao sobre
as requisies realizadas. Esta restrio importante para promover a escalabilidade do sistema,
pois diversas instncias da Web API podem ser iniciadas para realizar o balanceamento de carga.
Considerando que as requisies dos clientes sejam auto-descritivas, qualquer Web API pode atender
a qualquer requisio sem necessidade de compartilhamento de estados entre os servidores.

Jersey
Jersey a implementao de referncia da especificao JAX-RS, que estabelece os mecanismos
para o desenvolvimento de Web Services REST para a linguagem Java. O framework Jersey permite
a manipulao de requisies HTTP, a serializao de representaes de recursos em diversos
formatos, alm de mecanismos para tratamento de excees.
O cdigo de exemplo de utilizao do Jersey apresenta uma classe que recebe as anotaes do framework para manipular requisies HTTP. A anotao @Path(caminho1) aplicada diretamente
sobre a classe e determina uma URL de acesso. As anotaes @GET, @POST, @PUT e @DELETE so
utilizadas para associar os mtodos da classe aos respectivos verbos HTTP. As anotaes @Consumes
e @Produces especificam o formato das representaes que so esperadas e retornadas pelos mtodos,
respectivamente. Neste exemplo, as representaes sero serializadas em JSON. Atravs da anotao
@Path, aplicada sobre um mtodo, possvel adicionar trechos adicionais URL, alm de definir
variveis atravs de @PathParam ou de @QueryParam, como exemplificado no mtodo carregar. Os
valores das variveis do tipo @PathParam so atribudos como parte integrante da URL, enquanto
os valores de @QueryParam so associados aos nomes das variveis.

Introduo

Exemplo de anotaes Jersey


@Path("caminho1")
public class ExemploJersey {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("caminho2/{var1}")
public Response carregar(@PathParam("var1") String x, @QueryParam("var2") String y){
// codigo para carregar um recurso
String retorno = String.format("var1: %s var2: %s", x, y);
return Response.ok(retorno).build();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response criar() {
// codigo para criar um recurso
return Response.ok("mensagem de retorno").build();
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response modificar() {
// codigo para modificar um recurso
return Response.ok("mensagem de retorno").build();
}
@DELETE
public Response remover() {
// codigo para remover um recurso
return Response.ok("mensagem de retorno").build();
}
}

Exemplo de manipulao de variveis

Introduo

Spring Boot
Spring Boot um framework para o desenvolvimento de aplicaes baseadas em Spring. Sua
principal contribuio a facilidade de configurao do projeto e aumento de produtividade. Alm
disso, o Spring Boot uma das opes mais adotadas para o desenvolvimento de Web APIs em Java,
principalmente para a arquitetura de microservices. Caso voc no tenha experincia com o Spring
framework no se preocupe, pois uma breve introduo ser apresentada a seguir. Se voc domina
os conceitos bsicos do Spring, fique a vontade para seguir em frente.
Antes de falar sobre o Spring framework, primeiro vamos discutir um pouco sobre design de
software. Uma das formas mais tradicionais de modelagem o design em camadas, que agrupa
o sistema em classes que possuem a mesma responsabilidade, tais como: persistir informaes em
um banco de dados, aplicar regras de negcio ou interagir com os usurios. Alm disso, existe a
camada de domnio de aplicao, que descreve as informaes manipuladas pelo sistema, sendo
utilizada pelas demais camadas. Neste exemplo, a camada de persistncia de dados presta servios
para a camada de regras de negcio, que por sua vez, presta servios para a camada de integrao.
Os servios so descritos por meio de contratos, que estabelecem as diretrizes para a execuo das
funcionalidades.

Design em camadas

As camadas do sistema trocam mensagens atravs de um fluxo bem definido, como mostra a
figura a seguir. Ao receber uma requisio do usurio, a camada de integrao converte os dados
recebidos em um objeto de domnio (DOM). Em seguida, a informao (objeto de domnio) passada
para a camada de negcio atravs da construo de um objeto (NEG) e a invocao de uma de
suas funcionalidades descritas no seu contrato. Por sua vez, a camada de negcio aplica as regras
necessrias e solicita servios de persistncia (DAO). Por fim, a camada de persistncia recebe o
objeto de domnio e executa alguma operao de banco de dados.

Introduo

Interao entre camadas

Atravs deste modelo de interao, possvel dizer que a camada de integrao depende da camada
de negcios, que por sua vez, depende da camada de persistncia. Entretanto, para manter o baixo
acoplamento, as camadas se comunicam com base somente nos contratos de servio. As classes que
implementam os servios no devem ser compartilhadas entre as camadas. neste ponto que o
Spring framework entra em ao. Ele capaz de realizar a injeo de dependncias, que a partir do
contrato de servio (interface), cria um objeto que implementa esta interface.
A anotao @Component define uma classe como um bean do Spring que pode ser injetado em
outro bean, fazendo parte do contexto das classes gerenciadas pelo framework. A classe Integracao
apenas informa que depende de um objeto que implementa o contrato definido pela interface
Negocio. O mesmo ocorre na classe NegocioImpl, que depende de um objeto que implementa a
interface Dao. Estes pontos de injeo so demarcados atravs da anotao @Autowired, e durante o
carregamento da aplicao, o Spring framework providencia a criao dos objetos necessrios. Nas
prximas sees sero apresentados exemplos concretos que utilizam a injeo de dependncias.
A injeo de dependncias apenas uma das funcionalidades disponibilizadas pelo Spring. Vrios
outros mdulos fazem parte da pilha de tecnologias do framework. Neste projeto, ser utilizado
tambm o suporte JDBC do Spring, que facilita a manipulao de banco de dados, alm de oferecer
controle de transaes, fundamental para garantir a integridade dos dados.

Contexto Spring e injeo de dependncias

Introduo

fundamental compreender corretamente o comportamento dos beans dos Spring. Por padro,
quando o Spring cria uma instncia de um bean, este objeto segue o comportamento singleton,
onde apenas um objeto construdo e utilizado nos pontos de injeo. Ao anotar um endpoint com
@Component, adota-se o comportamento prototype, onde os valores dos atributos da classe sero
mantidos entre as requisies. Este entendimento sobre os beans do Spring fundamental para
garantir o comportamento correto da aplicao.

Endpoint singleton

Endpoint prototype

Projeto
Este captulo apresenta os detalhes do projeto de uma Web API que ser implementada com Spring
Boot e Jersey. Primeiramente apresentada a viso geral, seguida da modelagem do domnio da
aplicao e da integrao de dados, ferramentas utilizadas, contratos de servio e configurao de
banco de dados.

Viso Geral
Uma aplicao de gerenciamento de uma agenda de contatos utilizada como exemplo para aplicar
as tecnologias abordadas neste livro. Diversas simplificaes foram realizadas no projeto para
manter o foco nas tecnologias, simplificar o entendimento e a implementao. Embora o exemplo
seja baseado em um estudo de caso simples, o projeto apresenta os requisitos mais comuns em
aplicaes reais. O projeto contempla o desenvolvimento de uma Web API para a manipulao
de dados de contatos. Atravs de requisies HTTP, deve ser possvel cadastrar novos contatos,
consultar contatos anteriormente cadastrados, alm de alterar e remover os dados.

Modelagem do Domnio da Aplicao


O domnio da aplicao especifica quais so as informaes manipuladas pelo sistema. O domnio
constitudo apenas pelas classes Contato e Endereco. A aplicao deve gerenciar o nome, email,
cpf, telefone, telefone e o endereo dos contatos. O endereo formado pelo estado, cidade, bairro
e logradouro. As classes de domnio da aplicao so implementadas como Plain Old Java Object
(POJOs), constitudas apenas por atributos privados, construtor padro e mtodos acessores. Embora
esta modelagem resulte em objetos de domnio anmicos, ainda assim uma abordagem tradicional
e muito utilizada.

Modelo conceitual do domnio da aplicao

10

Projeto

Contato.java
public class Cliente {
private String id;
private String nome;
private String email;
private String cpf;
private String telefone;
private Date dataNascimento;
private Endereco endereco;
//gets e sets omitidos
}

Endereco.java
public class Endereco {
private String estado;
private String cidade;
private String bairro;
private String logradouro;
//gets e sets omitidos
}

Integrao de Dados
A integrao de dados representa a interface com o usurio do sistema, que no contexto de Web APIs
so outras aplicaes. Com base nas funcionalidades descritas na viso geral do projeto, pode-se
modelar as classes de integrao por meio de dois recursos. O recurso ListaDeContatos agrupa todos
os contatos cadastrados no sistema, e disponibiliza dois mtodos para interao. O primeiro mtodo
utiliza HTTP GET, que retorna a representao da lista ao usurio. O segundo mtododo utiliza
HTTP POST para adicionar um novo contato lista. O recurso Contato manipula as informaes de
um contato especfico, e disponibiliza trs mtodos de interao. O primeiro mtodo utiliza HTTP
GET que retorna os dados de um contato, enquanto o segundo e o terceiro mtodo utilizam HTTP
PUT e DELETE para alterar e remover um contato, respectivamente.

Recursos e mtodos para integrao de dados

11

Projeto

Ferramentas Utilizadas
A IDE utilizada para implementar o projeto foi o Eclipse verso Mars Release (4.5.0). Entretanto,
outras IDEs podem ser utilizada sem prejuzos para o desenvolvimento. O MySQL verso 5.5.46 foi
utilizado com SGBD da aplicao. Novamente, outros bancos de dados podem ser utilizados para
implementar o projeto. O Apache Maven foi utilizado para gerenciar o projeto. Foi utilizada a verso
que acompanha o Eclipse, sem necessidade de nenhuma instalao externa.

Configurao Inicial
Como dito anteriormente, o projeto gerenciado pelo Apache Maven. Para criar um projeto Maven
basta selecionar New > Maven Project no menu File. Atravs da seleo da opo Create a simple
project, ser criado um projeto Maven simples, sem nenhuma pr-configurao. Na prxima janela
sero preenchidas as informaes de Group Id e Artifact Id, que representam a organizao que
desenvolve o projeto, e o nome do projeto, respectivamente. Neste exemplo, o valor de Group Id
br.com.exemplo e de Artifact Id agenda-api. Embora o projeto seja uma aplicao Web, o Packaging
selecionado jar.
Com o projeto criado, o momento de organizar as classes em pacotes. O pacote config agrupa
todas as classes relacionadas com a configurao da aplicao. Os pacotes dao e negocio agrupam
as classes e interfaces de persistncia e regras de negcio, respectivamente. As classes do domnio
da aplicao so agrupadas no pacote dominio. As classes responsveis por manipular as requisies
dos usurios so agrupadas no pacote endpoint. Por fim, as representaes de recursos que exigem
algum tratamento de apresentao sero agrupadas no pacote representacao.

Novo projeto Maven parte 1

12

Projeto

Novo projeto Maven parte 2

Estrutura de pacotes

O projeto tambm possui dois arquivos de configurao: application.yml e pom.xml. O arquivo


application.yml responsvel por externalizar a configurao da aplicao, como por exemplo,
dados de conexo ao banco de dados, porta HTTP para receber as requisies, alm de outras
configuraes necessrias. No arquivo pom.xml so descritas as dependncias (bibliotecas) externas,
alm de diretrizes para compilao do projeto.

Projeto

pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source> <target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>
true
</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
</dependencies>

13

Projeto

14

Certifique-se de adicionar o contedo do arquivo pom.xml em seu projeto conforme o exemplo


anterior. Ao declarar as dependncias e salvar o arquivo, o Apache Maven se encarrega de realizar
o download das bibliotecas e importar ao projeto. Este procedimento pode levar alguns minutos.
Quando o arquivo pom.xml modificado, necessrio atualizar o projeto da seguinte forma: clique
com o boto direito do mouse sobre o nome do projeto, em seguida selecione o menu Maven > Update
Project. na prxima janela certifique-se de que o projeto est selecionado e confirme.
Com o projeto criado e as bibliotecas configuradas, hora de configurar o framework Jersey. Crie
uma classe no pacote config conforme descrito em JerseyConfig.java. A anotao @ApplicationPath
define a URL padro da aplicao. Todos os endpoints da aplicao devem ser registrados. Um
endpoint pode ser registrado individualmente ou pode-se registrar todos os endpoints de um pacote.
A opo escolhida foi registrar o pacote endpoint, dessa forma, todos os endpoints deste pacote esto
automaticamente registrados.
JerseyConfig.java
@Component
@ApplicationPath("/agenda-api")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
this.register(RequestContextFilter.class);
this.packages("br.com.exemplo.agenda.api.endpoint");
}
}

O Spring Boot permite que uma aplicao Web seja executada a partir de um arquivo jar executvel,
semelhante a uma aplicao stand-alone. Sendo assim, preciso implementar uma classe que implementa o mtodo main. Crie uma classe no pacote config com o contedo de WebApiApplication.java.
A anotao @SpringBootApplication define a classe como a responsvel por iniciar a aplicao.
A anotao @ComponentScan recebe o nome do pacote para iniciar a varredura dos beans do
Spring anotados com @Component. De acordo com o exemplo, a varredura contempla todos os
pacotes a partir de br.com.exemplo.agenda.api, localizando beans nos pacotes, config, dao, dominio,
endpoint, negocio e representacao.
WebApiApplication.java
@SpringBootApplication
@ComponentScan("br.com.exemplo.agenda.api")
public class WebApiApplication {
public static void main(String[] args) {
SpringApplication.run(WebApiApplication.class, args);
}
}

Projeto

15

Neste momento a aplicao est pronta para manipular requisies HTTP. Vamos fazer um teste
para verificar se tudo est configurado corretamente. Crie uma classe no pacote endpoint com o
contedo de TesteEndPoint.java. Este endpoint manipula requisies HTTP GET mapeadas para a
URL teste. O resultado da requisio uma mensagem de texto informando que o teste foi bem
sucedido. Os endpoints so acessados atravs de URLs resultantes da concatenao da URL base
definida na configurao do Jersey com os caminhos definidos em cada endpoint e seus respectivos
mtodos. Por padro, o Spring Boot utiliza a porta HTTP 8080. Para realizar o teste, execute a classe
main* e digite a seguinte URL em seu navegador: localhost:8080/agenda-api/teste.
TesteEndPoint.java
Path("teste")
public class TesteEndPoint {
@GET
public Response teste() {
return Response.ok("Teste bem sucedido").build();
}
}

Contratos
Os contratos so as descries dos servios prestados pelas camadas do sistema. Na linguagem de
programao Java, os contratos so desenvolvidos atravs de interfaces. A seguir, so definidos
os dois contratos da camada de persistncia. Cada contrato descreve os servios de persistncia
associados a uma classe de domnio da aplicao. Sendo assim, o contrato especificado em ContatoDao.java define os servios de persistncia para a classe Contato, enquanto o contrato especificado
em EnderecoDao.java define os servios para a classe Endereco.
ContatoDao.java
public interface ContatoDao {
void cadastrar(Contato contato);
void alterar(Contato contato);
void remover(String idContato);
Contato consultar(String idContato);
List<Contato> listarTodos();
}

16

Projeto

EnderecoDao.java
public interface EnderecoDao {
void cadastrar(Endereco endereco, String idContato);
Endereco consultar(String idContato);
void remover(String idContato);
}

Apenas um contrato estabelecido na camada de negcio, como especificado em RegrasContatos.java. Este contrato considera que o objeto contato composta por um objeto endereco. Dessa
forma, o endereo manipulado juntamente com os dados do contato, mesmo que persistido de
forma independente.
RegrasContatos.java
public interface RegrasContatos {
void cadastrar(Contato contato);
List<Contato> listarTodos();
Contato consultar(String idContato);
public void alterar(Contato contato);
public void remover(String idContato);
}

Configurao do Banco de Dados


A primeira parte da configurao a criao da base de dados utilizada pela aplicao. As tabelas
do banco de dados foram criadas com base nas classes de domnio da aplicao. Seguindo o domnio
da aplicao, foram criadas duas tabelas, uma para armazenar os dados do contato e outra para o
endereo. A tabela endereco no possui chave primria, apenas uma chave estrangeira relacionada
ao id do contato.

Diagrama do banco de dados

Projeto

17

A prxima parte da configurao adicionar as dependncias do driver JDBC - MySQL e do mdulo


Spring JDBC ao arquivo pom.xml. O driver JDBC uma biblioteca necessria para que uma aplicao
Java se comunique com o sistema de banco de dados. O mdulo Spring JDBC oferece uma srie de
mecanismos para facilitar e aumentar a produtividade no desenvolvimento de classes que interagem
com o banco de dados.
Dependncias para manipular banco de dados (pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>

Por fim, as configuraes de acesso ao banco de dados, como por exemplo: URL, porta, nome de
usurio e senha, devem ser realizadas no arquivo application.yml. Alm das configuraes bsicas,
apresentada a configurao necessria para realizar a verificao das conexes, evitando que a
aplicao utilize uma conexo invlida.
Configurao do banco de dados (application.yml)
spring.datasource.url: "jdbc:mysql://<enderecoServidor>:3306"
spring.datasource.username: <usuario>
spring.datasource.password: <senha>
spring.datasource.driver-class-name: com.mysql.jdbc.Driver
spring.datasource.max-active: 10
spring.datasource.initial-size: 5
spring.datasource.max-idle: 5
spring.datasource.min-idle: 1
spring.datasource.test-while-idle: true
spring.datasource.test-on-borrow: true
spring.datasource.validation-query: "SELECT 1"
pring.datasource.time-between-eviction-runs-millis: 5000
spring.datasource.min-evictable-idle-time-millis: 60000

Implementao das Funcionalidades


Chegou a hora de implementar as funcionalidades da aplicao. Este captulo apresenta os detalhes
para implementar a agenda de contatos, contemplando todo o ciclo de vida das informaes.
De acordo com as especificaes estabelecidas anteriormente, so apresentados os detalhes de
implementao das funcionalidades de cadastramento, consulta, alterao e remoo de contatos.

Cadastramento
O cadastramento a funcionalidade responsvel por criar um novo contato na agenda. O processo
de cadastramento iniciado atravs de uma requisio HTTP, que solicita que a representao
informada seja mantida no banco de dados. Sendo assim, vamos iniciar a implementao pelo
endpoint responsvel por manipular as requisies do usurio.
Crie uma classe no pacote endpoint com o contedo de ListaContatosEndpoint.java. A anotao
@Path(listaDeContatos) define que a classe tem o comportamento de endpoint e estabelece uma
URL de acesso. Nas linhas 4 e 5 demarcado um ponto de injeo de dependncia para uma instncia
de objeto que representa as regras de negcio. Este o ponto onde a informao passa da camada de
integrao para a camada de negcio. Entre as linhas 7 e 13 implementado o mtodo que recebe
a requisio para o cadastro do contato. O mtodo anotado com @POST, que define o verbo
HTTP a ser utilizado. As anotaes @Produces e @Consumes definem o JSON como formato de
representao de entrada e de sada do mtodo. O mtodo recebe um objeto que automaticamente
convertido de JSON para um objeto do tipo Contato. Na sequncia, invocado o mtodo cadastrar
do contrato das regras de negcio e retornado ao usurio o objeto armazenado no banco de dados.
O prximo passo implementar a classe responsvel pelas regras de negcio para o cadastramento
de contatos. O cdigo apresentado em GerenciadorContatos.java apresenta a classe que implementa
a interface RegrasContatos com todos os mtodos definidos no contrato. Deve-se anotar a classe com
@Component para defini-la como um bean do Spring. O cadastramento exige a manipulao das
informaes do contato e de seu endereo. Sendo assim, nas linhas 4 a 8 so definidos os pontos de
injeo de dependncia para os Daos responsveis pela persistncia dos dados. O mtodo cadastrar
apenas gera um id aleatrio para o contato, alm de solicitar para a camada de persistncia o
armazenamento das informaes.

18

Implementao das Funcionalidades

19

ListaContatosEndpoint.java - cadastramento
1
2

@Path("listaDeContatos")
public class ListaContatosEndpoint {

@Autowired
private RegrasContatos regrasContatos;

4
5
6

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response cadastrarContato(ContatoRep contato) {
regrasContatos.cadastrar(contato);
return Response.ok(contato).build();
}

7
8
9
10
11
12
13
14

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response carregarListaContatos() {...}

15
16
17
18

Note que os mtodos cadastrar, alterar e remover so anotados com @Transactional. Esta anotao
define uma transao de negcio, que garante a execuo atmica de todas as instrues do mtodo.
Caso alguma exceo seja lanada durante a execuo do mtodo, o Spring framework garante o
retorno do banco de dados para o estado inicial da transao.
Imagine o seguinte cenrio onde no seja definida uma transao. Na linha 15 de GerenciadorContatos.java, solicitada a gravao das informaes do contato no banco de dados. Considere que
as informaes foram persistidas na tabela de contatos. Durante a gravao dos dados de endereo
(linha 16) ocorre alguma exceo que no permita a persistncia na tabela de endereo. Entretanto,
existe uma restrio que todo contato deve obrigatoriamente possuir informaes do endereo.
Neste cenrio, o banco de dados est em um estado de inconsistncia, pois os dados do contato
foram armazenados sem os dados de endereo. Por outro lado, quando o mtodo anotado com
@Transactional, a transao garante que os dados armazenados na tabela do contato sejam desfeitos,
resultando no rollback automtico dos dados.

Implementao das Funcionalidades

20

GerenciadorContatos.java - cadastramento
1
2

@Component
public class GerenciadorContatos implements RegrasContatos {

@Autowired
private ContatoDao contatoDao;

4
5
6

@Autowired
private EnderecoDao enderecoDao;

7
8
9

@Override
@Transactional
public void cadastrar(Contato contato) {
String idContato = UUID.randomUUID().toString();
contato.setId(idContato);
contatoDao.cadastrar(contato);
enderecoDao.cadastrar(contato.getEndereco(), idContato);
}

10
11
12
13
14
15
16
17
18

@Override
public List<Contato> listarTodos() {...}

19
20
21

@Override
public Contato consultar(String idContato) {...}

22
23
24

@Override
@Transactional
public void alterar(Contato contato) {...}

25
26
27
28

@Override
@Transactional
public void remover(String idContato) {...}

29
30
31
32

Os arquivos JdbcContatoDao.java e JdbcEnderecoDao.java apresentam a implementao das classes


de persistncia do contato e do endereo. A manipulao do banco de dados realizada atravs do
mdulo Spring JDBC. Alm disso, ambas as classes so anotadas com @Component para que possam
ser injetadas nas instncias que necessitam dos servios de persistncia.

Implementao das Funcionalidades

JdbcContatoDao.java - cadastramento
@Component
public class JdbcContatoDao implements ContatoDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public void cadastrar(Contato contato) {
StringBuilder sql = new StringBuilder();
sql.append("insert into agenda.contato ");
sql.append("(id, nome, email, cpf, telefone, data_nascimento) ");
sql.append("values (:id, :nome, :email, :cpf, :tel, :dataN)");
Map<String, Object> parametros = new HashMap<>();
parametros.put("id", contato.getId());
parametros.put("nome", contato.getNome());
parametros.put("email", contato.getEmail());
parametros.put("cpf", contato.getCpf());
parametros.put("tel", contato.getTelefone());
parametros.put("dataN", contato.getDataNascimento());
jdbcTemplate.update(sql.toString(), parametros);
}
@Override
public List<Contato> listarTodos() {...}
@Override
public Contato consultar(String idContato) {...}
@Override
public void alterar(Contato contato) {...}
@Override
public void remover(String idContato) {...}
}

21

Implementao das Funcionalidades

22

JdbcEnderecoDao.java - cadastramento
@Component
public class JdbcEnderecoDao implements EnderecoDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public void cadastrar(Endereco endereco, String idContato) {
StringBuilder sql = new StringBuilder();
sql.append("insert into agenda.endereco ");
sql.append("(estado, cidade, bairro, logradouro, id_contato) ");
sql.append("values (:estado, :cidade, :bairro, :logradouro, :idContato)");
Map<String, Object> parametros = new HashMap<>();
parametros.put("idContato", idContato);
parametros.put("estado", endereco.getEstado());
parametros.put("cidade", endereco.getCidade());
parametros.put("bairro", endereco.getBairro());
parametros.put("logradouro", endereco.getLogradouro());
jdbcTemplate.update(sql.toString(), parametros);
}
@Override
public Endereco consultar(String idContato) {...}
@Override
public void remover(String idContato) {...}
}

O suporte do Spring JDBC disponibilizada atravs de um NamedParameterJdbcTemplate injetado


diretamente nas classes Dao. Este objeto oferece a abstrao necessria para executar instrues
SQL. Utilizando o Stringbuilder, escrita uma instruo SQL para inserir os dados no banco. As
variveis so associadas chaves precedidas por dois pontos :. Em seguida, construdo um mapa
para relacionar as chaves aos valores extrados do objeto de domno contato. Por fim, invocado o
mtodo update que recebe a String que representa o SQL e o mapa de parmetros. O armazenamento
do endereo segue o mesmo procedimento, modificando apenas o comando SQL e a extrao das
informaes do objeto de domnio relacionado ao endereo.
Para testar a implementao do cadastro de contatos necessrio realizar uma requisio HTTP
POST. Para isso, vamos utilizar uma extenso de navegador chamada Postman. A parte superior da
ferramenta mostra os dados enviados para Web API, enquanto a parte inferior mostra as informaes

Implementao das Funcionalidades

23

retornadas. A mensagem de retorno contm os dados enviados com a adio do id gerado pela
aplicao. Entretanto, possvel notar que a data de nascimento retornou com um valor diferente.
Analisando com mais detalhes, vamos at o banco de dados para verificar como o registro foi
armazenado. Como verificado, a data de nascimento foi armazenada com um valor incorreto (200010-09), sendo que o valor informado foi 2000-10-10. Isto ocorre devido converso direta de
uma String para um objeto do tipo Date. Uma forma de corrigir este erro converter os dados
manualmente para uma representao personalizada do recurso, ao invs de utilizar diretamente o
objeto de domnio da aplicao.

Requisio de teste para cadastramento de contato

Implementao das Funcionalidades

24

Registro de teste armazenado no banco de dados

Criao de Representaes
Representaes so classes de apoio para a troca de informaes entre Web APIs e seus clientes. Elas
so utilizadas em situaes em que compartilhar diretamente os objetos de domnio da aplicao no
adequado, principalmente quando necessrio atender questes de formatao de dadas, valores
numricos ou a prpria estrutura das informaes.
A classe ContatoRep.java mostra o cdigo da representao do contato e de seu endereo. Todos os
atributos so valores textuais, inclusive a data de nascimento. Dessa forma, as informaes enviadas
pelos clientes sero tratadas como String e convertidas adequadamente. Outra diferena entre a
representao e as classes de domnio da aplicao a forma como os atributos esto estruturados,
pois todos os atributos esto organizados de forma plana, sem composio de classes. Sendo
assim, possvel criar diversas representaes para atender diferentes expectativas e propsitos das
aplicaes clientes.
ContatoRep.java
1
2
3
4
5
6
7
8
9
10
11

public class ContatoRep {


private String id;
private String nome;
private String email;
private String cpf;
private String telefone;
private String dataNascimento;
private String estado;
private String cidade;
private String bairro;
private String logradouro;

12
13

public ContatoRep() {}

14
15
16
17
18

public ContatoRep(Contato contato) {


this.id = contato.getId();
this.nome = contato.getNome();
this.email = contato.getEmail();

Implementao das Funcionalidades


this.cpf = contato.getCpf();
this.telefone = contato.getTelefone();
this.dataNascimento = serializarData(contato.getDataNascimento());

19
20
21
22

if (contato.getEndereco() != null) {
this.estado = contato.getEndereco().getEstado();
this.cidade = contato.getEndereco().getCidade();
this.bairro = contato.getEndereco().getBairro();
this.logradouro = contato.getEndereco().getLogradouro();
}

23
24
25
26
27
28

29
30

public Contato converterParaDominio() {


Contato contato = new Contato();
contato.setId(this.id);
contato.setNome(this.nome);
contato.setEmail(this.email);
contato.setCpf(this.cpf);
contato.setTelefone(this.telefone);

31
32
33
34
35
36
37
38

Date dataN = converterData(this.dataNascimento);


contato.setDataNascimento(dataN);

39
40
41

Endereco endereco = new Endereco();


endereco.setEstado(this.estado);
endereco.setCidade(this.cidade);
endereco.setBairro(this.bairro);
endereco.setLogradouro(this.logradouro);
contato.setEndereco(endereco);
return contato;

42
43
44
45
46
47
48

49
50

private Date converterData(String dataTextual) {


DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTime dataConvertida = dtf.parseDateTime(dataTextual);
return dataConvertida.toDate();
}

51
52
53
54
55
56

private String serializarData(Date data) {


DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy");
LocalDateTime dt = new LocalDateTime(data, DateTimeZone.UTC);
return dtf.print(dt);
}
//gets e sets omitidos

57
58
59
60
61
62
63

25

Implementao das Funcionalidades

26

Alm do construtor padro, est disponvel um construtor que preenche os atributos a partir de
um objeto de domnio, alm de um mtodo conversor de representao para domnio. Existe
tambm o conversor para manipular datas. O mtodo converterData converte uma data representada
textualmente no formato dd/MM/yyyy, em um objeto do tipo Date. A converso inversa realizada
pelo mtodo serializarData capaz de transformar em String um objeto do tipo Date. Para manipular a
data foi utilizada a biblioteca joda-time. Entretanto, necessrio incluir ao pom.xml esta dependncia
Dependncia joda-time (pom.xml)
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>

O prximo passo substituir a classe de domnio pela representao em ListaContatosEndpoint.java.


O mtodocadastrarContato recebe agora uma representao e no mais um objeto de domnio. Na
linha 5 realizada a converso da representao para o domnio, que repassado para a camada de
negcio, seguindo o fluxo previamente estabelecido.
ContatoRep.java - cadastramento
1
2
3
4
5
6
7
8
9

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response cadastrarContato(ContatoRep contato) {
Contato contatoDominio = contato.converterParaDominio();
regrasContatos.cadastrar(contatoDominio);
ContatoRep contatoCadastrado = new ContatoRep(contatoDominio);
return Response.ok(contatoCadastrado).build();
}

Em seguida, realiza-se a requisio para cadastro do contato utilizando a nova estrutura da


representao. Note que a data foi enviada respeitando o formato esperado, e a representao
retornada pela Web API est correta. Por fim, verifica-se que as informaes do contato foram
corretamente armazenadas no banco de dados.

Implementao das Funcionalidades

Cadastro de contato atravs da representao

Informaes armazenadas no banco de dados

27

Implementao das Funcionalidades

28

Consulta a Todos os Contatos


A prxima funcionalidade a ser implementada a consulta de todos os contatos cadastrados. A classe
ListaContatosEndpoint implementa o mtodo carregarListaContatos, associado ao verbo HTTP
GET, que retorna a lista de todos os contatos serializados em JSON. O mtodo obtm os contatos
atravs da invocao de um servio da camada de negcio (linha 12). Na linha 13, criada uma lista
responsvel por agrupar as representaes dos objetos de domnio. Entre as linhas 14 e 16 todos os
objetos de domnio so convertidos em representaes. Por fim, a lista de representaes retornada
ao cliente.
ListaContatosEndpoint.java - consultar todos os contatos cadastrados
1
2

@Path("listaDeContatos")
public class ListaContatosEndpoint {

@Autowired
private RegrasContatos regrasContatos;

4
5
6

//implementacao do cadastramento omitida

7
8

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response carregarListaContatos() {
List<Contato> lista = regrasContatos.listarTodos();
List<ContatoRep> representacoes = new ArrayList<>();
for (Contato contato : lista) {
representacoes.add(new ContatoRep(contato));
}
return Response.ok(representacoes).build();
}

9
10
11
12
13
14
15
16
17
18
19

O prximo passo implementar em GerenciadorContatos.java a regra de negcio para listagem de


todos os contatos. Nenhuma restrio definida, sendo assim, a camada de negcio apenas solicita
o servio da camada de persistncia para carregar os objetos desejados.

Implementao das Funcionalidades

29

GerenciadorContatos.java - consultar todos os contatos cadastrados


@Component
public class GerenciadorContatos implements RegrasContatos {
@Autowired
private ContatoDao contatoDao;
@Override
public List<Contato> listarTodos() {
return contatoDao.listarTodos();
}
//demais metodos omitidos
}

A ultima parte da funcionalidade a implementao da consulta de todos os registros no banco


de dados. A classe JdbcContatoDao mostra a utilizao do Spring JDBC para a recuperao de
informaes do banco de dados. Primeiramente, a instruo SQL de consulta construda nas linhas
9 e 10. Na sequncia, o objeto jdbcTemplate injetado pelo Spring executa o SQL e constri um
objeto de domnio com base em um RowMapper. O RowMapper implementa o mtodo mapRow
que cria e popula um objeto de domnio atravs da manipulao do resultSet. O mtodo rowMap
executado para cada registro retornado pelo banco de dados, que armazenado em uma lista. Note
que o endereo no est sendo carregado juntamente com o contato. Estas informaes somente
esto disponveis quando consultada as informaes de um contato especfico, que ser a prxima
funcionalidade a ser implementada.
Para finalizar, vamos realizar uma requisio atravs do Postman para consultar todos os contatos
cadastrados. Por meio de uma requisio HTTP GET na URL localhost:8080/agenda-api/listaDeContatos a Web API retorna um documento JSON com as informaes cadastradas.
JdbcContatoDao.java - consultar todos os contatos cadastrados
1
2

@Component
public class JdbcContatoDao implements ContatoDao {

3
4
5

@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;

6
7
8
9
10

@Override
public List<Contato> listarTodos() {
StringBuilder sql = new StringBuilder();
sql.append("select * from agenda.contato");

11
12
13
14

return jdbcTemplate.query(sql.toString(), new RowMapper<Contato>() {


@Override
public Contato mapRow(ResultSet rs, int rowNum) throws SQLException {

30

Implementao das Funcionalidades


Contato contato = new Contato();
contato.setId(rs.getString("id"));
contato.setNome(rs.getString("nome"));
contato.setEmail(rs.getString("email"));
contato.setCpf(rs.getString("cpf"));
contato.setTelefone(rs.getString("telefone"));
contato.setDataNascimento(rs.getDate("data_nascimento"));
return contato;

15
16
17
18
19
20
21
22

}
});

23
24

}
//demais metodos omitidos

25
26
27

Requisio para listar todos os contatos

Implementao das Funcionalidades

31

Consulta a um Contato Especfico


Esta funcionalidade consulta as informaes de um contato especfico. A classe ContatoEndpoint
implementa o endpoint que manipula as requisies destinadas a manipular um nico contato da
agenda. Como descrito anteriormente, este endpoint disponibiliza as funcionalidades para consulta,
alterao e remoo de recursos.
ContatoEndpoint.java - consulta a um contato especfico
1
2

@Path("contato")
public class ContatoEndpoint {

@Autowired
private RegrasContatos regrasContatos;

4
5
6

@QueryParam("idContato")
private String idContato;

7
8
9

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response obterContato() {
Contato contato = regrasContatos.consultar(idContato);
return Response.ok(new ContatoRep(contato)).build();
}

10
11
12
13
14
15
16

@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response alterarContato(ContatoRep contato) {...}

17
18
19
20
21

@DELETE
@Produces(MediaType.TEXT_PLAIN)
public Response removerContato() {...}

22
23
24
25

Na linha 1, a URL contato associada ao endpoint. As linhas 4 e 5 definem o ponto de injeo do


objeto responsvel pelas regras de negcio. O contato especificado atravs de seu identificador,
definido pela propriedade idContato. O identificador informado atravs de QueryParam, e a
varivel fica disponvel a todos os mtodos, como mostrado nas linhas 7 e 8. O parmetro idContato
poderia utilizar PathParam, entretanto, foi escolhido o formato *QueryParam apenas como uma
opo. Alm disso, a declarao da varivel idContato e a anotao de mapeamento QueryParam
podem ser realizadas na assinatura do mtodo.
Entre as linhas 10 e 15 implementado o mtodo de consulta aos dados do contato. O mtodo recebe
a anotao @GET alm definir o JSON como formato da representao retornada. Em seguida,

Implementao das Funcionalidades

32

invocado o mtodo consultar disponibilizado pelo objeto de negcio, que retorna um objeto de
domnio correspondente ao contato desejado. Por fim, retornada a representao do recurso a
partir do objeto de domnio. Os demais mtodos (linhas 17 a 25) sero implementados nas prximas
sees.
A classe GerenciadorContatos implementa a regra de negcio para esta consulta. O mtodo consultar
no aplica nenhuma restrio de negcio, apenas solicita camada de persistncia que consulte as
informaes do contato e de seu respectivo endereo. Por fim, o endereo associado ao contato e
retornado ao endpoint.
GerenciadorContatos.java - consulta a um contato especfico
1
2
3
4
5
6
7
8
9
10
11

@Component
public class GerenciadorContatos implements RegrasContatos {
@Override
public Contato consultar(String idContato) {
Contato contato = contatoDao.consultar(idContato);
Endereco endereco = enderecoDao.consultar(idContato);
contato.setEndereco(endereco);
return contato;
}
//demais metodos omitidos
}

A implementao dos mtodos que consultam as informaes do contato e do endereo no banco de


dados realizada nas classes JdbcContatoDao e JdbcEnderecoDao, respectivamente. Primeiramente,
construdo o comando SQL de consulta e associado o parmetro para identificador do contato.
Em seguida, executado o mtodo queryForObject disponibilizado pelo jdbcTemplate, que retorna
um nico objeto. O registro retornado pelo banco de dados manipulado por um RowMapper,
responsvel por construir um objeto de domnio e popular os atributos com os dados do resultset.

Implementao das Funcionalidades

JdbcContatoDao.java - consulta a um contato especfico


@Component
public class JdbcContatoDao implements ContatoDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public Contato consultar(String idContato) {
StringBuilder sql = new StringBuilder();
sql.append("select * ");
sql.append("from agenda.contato ");
sql.append("where id = :id");
MapSqlParameterSource params = new MapSqlParameterSource("id", idContato);
return jdbcTemplate.queryForObject(sql.toString(), params, new RowMapper<Contato>() {
@Override
public Contato mapRow(ResultSet rs, int rowNum) throws SQLException {
Contato contato = new Contato();
contato.setId(rs.getString("id"));
contato.setNome(rs.getString("nome"));
contato.setEmail(rs.getString("email"));
contato.setCpf(rs.getString("cpf"));
contato.setTelefone(rs.getString("telefone"));
contato.setDataNascimento(rs.getDate("data_nascimento"));
return contato;
}
});
}
//demais metodos omitidos
}

33

Implementao das Funcionalidades

34

JdbcEnderecoDao.java - consulta a um contato especfico


@Component
public class JdbcEnderecoDao implements EnderecoDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public Endereco consultar(String idContato) {
StringBuilder sql = new StringBuilder();
sql.append("select * ");
sql.append("from agenda.endereco ");
sql.append("where id_contato= :id");
MapSqlParameterSource params = new MapSqlParameterSource("id", idContato);
return jdbcTemplate.queryForObject(sql.toString(), params, new RowMapper<Endereco>() {
@Override
public Endereco mapRow(ResultSet rs, int rowNum) throws SQLException {
Endereco endereco = new Endereco();
endereco.setBairro(rs.getString("bairro"));
endereco.setCidade(rs.getString("cidade"));
endereco.setEstado(rs.getString("estado"));
endereco.setLogradouro(rs.getString("logradouro"));
return endereco;
}
});
}
}

Para testar a funcionalidade vamos executar uma requisio no Postman com HTTP GET aplicada
sobre a URL localhost:8080/agenda-api/contato, adicionando o queryParam idContato com o
identificador desejado. importante adicionar o cabealho HTTP Content-Type configurado para
application/json.

Implementao das Funcionalidades

35

Requisio para listar um contato especfico

Alterao e Remoo
As ltimas funcionalidades que faltam ser implementadas so a alterao e a remoo dos contatos
cadastrados. Vamos comear com a implementao da classe ContatoEndpoint. O identificador do
contato atribudo varivel idContato por meio da anotao QueryParam, utilizada por ambos
os mtodos. Alm disso, os dois mtodos respeitam a semntica do protocolo HTTP e utilizam PUT
para alterao e DELETE para remoo de recursos.
O mtodo alterarContato recebe do cliente uma representao com os dados atualizados do contato.
Note que o identificador obtido a partir do atributo associado ao QueryParam deve ser atribudo ao
objeto, e convertido para o modelo de domnio antes de ser repassado camada de negcio. O mtodo
removerCliente apenas utiliza o identificador para solicitar a remoo do contato. Ao remover um
contato, a Web API retorna apenas uma mensagem de sucesso, sendo assim, a anotao @Produces
define o formato da representao como texto plano.

Implementao das Funcionalidades

36

ContatoEndpoint.java - alterao e remoo


@Path("contato")
public class ContatoEndpoint {
@Autowired
private RegrasContatos regrasContatos;
@QueryParam("idContato")
private String idContato;
//metodo de obtencao de um cliente omitido
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response alterarContato(ContatoRep contato) {
contato.setId(idContato);
Contato contatoDominio = contato.converterParaDominio();
regrasContatos.alterar(contatoDominio);
return Response.ok(new ContatoRep(contatoDominio)).build();
}
@DELETE
@Produces(MediaType.TEXT_PLAIN)
public Response removerContato() {
regrasContatos.remover(idContato);
return Response.ok("Contato removido com sucesso").build();
}
}

A classe GerenciadorContatos implementa os mtodos da camada de negcio para alterao e


remoo. Ambos os mtodos recebem a anotao @Transactional, pois deve-se garantir que os
dados do cliente e do endereo sejam alterados ou removidos de forma atmica. A camada de
negcio apenas solicita servios da camada de persistncia. Entretanto poderiam ser implementadas
restries para garantir determinadas regras de negcio.
A camada de persistncia no oferece o servio de alterao de endereo. Sendo assim, a alterao
deve remover o endereo e depois cadastra-lo novamente. Esta uma caracterstica importante,
pois as funcionalidades disponibilizadas pelas diferentes camadas no precisam implementar necessariamente as mesmas funcionalidades. Cada camada disponibiliza servios de forma relativamente
independente.

Implementao das Funcionalidades

37

GerenciadorContatos.java - alterao e remoo


@Component
public class GerenciadorContatos implements RegrasContatos {
@Autowired
private ContatoDao contatoDao;
@Autowired
private EnderecoDao enderecoDao;
@Override
@Transactional
public void alterar(Contato contato) {
contatoDao.alterar(contato);
enderecoDao.remover(contato.getId());
enderecoDao.cadastrar(contato.getEndereco(), contato.getId());
}
@Override
@Transactional
public void remover(String idContato) {
enderecoDao.remover(idContato);
contatoDao.remover(idContato);
}
//demais metodos omitidos
}

A classe JdbcContatoDao implementa os mtodos que manipulam o banco de dados para alterar e
para remover um contato. Os dois mtodos utilizam o mesmo princpio: primeiramente construdo
o comando SQL com base nas variveis do objeto de domnio; em seguida criado um mapa
com os parmetros; por fim, o comando SQL executado com base no mapa dos parmetros.
A remoo de um endereo, funcionalidade implementada pela classe JdbcEnderecoDao, segue o
mesmo procedimento.

Implementao das Funcionalidades

JdbcContatoDao.java - alterao e remoo


Component
public class JdbcContatoDao implements ContatoDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public void alterar(Contato contato) {
StringBuilder sql = new StringBuilder();
sql.append("update agenda.contato set ");
sql.append("nome= :nome, ");
sql.append("email= :email, ");
sql.append("cpf= :cpf, ");
sql.append("telefone= :telefone, ");
sql.append("data_nascimento= :dataNascimento ");
sql.append("where id=:id");
Map<String, Object> parametros = new HashMap<>();
parametros.put("id", contato.getId());
parametros.put("nome", contato.getNome());
parametros.put("email", contato.getEmail());
parametros.put("cpf", contato.getCpf());
parametros.put("telefone", contato.getTelefone());
parametros.put("dataNascimento", contato.getDataNascimento());
jdbcTemplate.update(sql.toString(), parametros);
}
@Override
public void remover(String idContato) {
StringBuilder sql = new StringBuilder();
sql.append("delete from agenda.contato ");
sql.append("where id = :id");
MapSqlParameterSource params = new MapSqlParameterSource("id", idContato);
jdbcTemplate.update(sql.toString(), parametros);
}
//demais metodos omitidos
}

38

39

Implementao das Funcionalidades

JdbcEnderecoDao.java - remoo
@Component
public class JdbcEnderecoDao implements EnderecoDao {
@Override
public void remover(String idContato) {
StringBuilder sql = new StringBuilder();
sql.append("delete from agenda.endereco ");
sql.append("where id_contato = :idContato");
MapSqlParameterSource params = new MapSqlParameterSource("id", idContato);
jdbcTemplate.update(sql.toString(), params);
}
//demais metodos omitidos
}

Requisio para alterar um contato

40

Implementao das Funcionalidades

Requisio para remover um contato

Tratamento de Excees
Quando desenvolvemos sistemas, temos em mente que tudo ir funcionar perfeitamente. Entretanto,
no podemos ignorar o fato que erros e situaes no planejadas podem e iro acontecer. Quando o
sistema atinge um estado de no conformidade, por exemplo: um erro de execuo de um comando
SQL, ou alguma informao invlida proveniente do usurio, so lanadas excees, que se no
tratadas adequadamente se transformam em erros do sistema. Este captulo apresenta como definir
as excees de negcio e como tratar os erros do sistema.

Criao de Excees de Negcio


Excees de negcios so lanadas quando alguma restrio do prprio domnio da aplicao no
so respeitadas. Vamos criar a seguinte restrio de negcio: Apenas contatos com idade igual ou
superior a 18 anos podem ser cadastrados. Quando solicitado o cadastro de um contato que no
atenda a esta regra, dever ser lanada uma exceo.
A classe IdadeContatoException implementa uma exceo relacionada idade mnima para o cadastro de contatos. Sem entrar no mrito de excees checadas ou no checadas, vamos implementar as
excees atravs da herana de RuntimeException. Neste projeto, as classes de exceo so agrupadas
no pacote negocio.
IdadeContatoException.java
public class IdadeContatoException extends RuntimeException {
public IdadeContatoException(String msg) {
super(msg);
}
}

Vamos implementar agora a verificao da data de nascimento no momento em que o contato


cadastrado e alterado. A classe GerenciadorContatos mostra a implementao do mtodo privado
validarDataNascimento, que calcula a quantidade de anos entre a data de nascimento do contato e
a data atual, lanando a exceo caso a diferena seja menor que a idade mnima estabelecida. Os
mtodos cadastrar e alterar incluem agora a verificao da data de nascimento.

41

Tratamento de Excees

GerenciadorContatos.java
@Component
public class GerenciadorContatos implements RegrasContatos {
private final int IDADE_MINIMA = 18;
@Autowired
private ContatoDao contatoDao;
@Autowired
private EnderecoDao enderecoDao;
@Override
@Transactional
public void cadastrar(Contato contato) {
validarDataNascimento(contato.getDataNascimento());
String idContato = UUID.randomUUID().toString();
contato.setId(idContato);
contatoDao.cadastrar(contato);
enderecoDao.cadastrar(contato.getEndereco(), idContato);
}
@Override
@Transactional
public void alterar(Contato contato) {
validarDataNascimento(contato.getDataNascimento());
contatoDao.alterar(contato);
enderecoDao.remover(contato.getId());
enderecoDao.cadastrar(contato.getEndereco(), contato.getId());
}
private void validarDataNascimento(Date dataNascimento) {
DateTime dateTimeDn = new DateTime(dataNascimento);
DateTime hoje = new DateTime();
int idade = Years.yearsBetween(dateTimeDn, hoje).getYears();
if (idade < IDADE_MINIMA) {
String msgErro = "Contato com menos de %s anos";
msgErro = String.format(msgErro, IDADE_MINIMA);
throw new IdadeContatoException(msgErro);
}
}
//demais metodos omitidos
}

42

Tratamento de Excees

43

Uma vez lanada, a exceo precisa ser tratada para disponibilizar uma resposta adequada ao cliente
da aplicao. Uma forma envolver a chamada do mtodo de negcio com try/catch. O exemplo a
seguir mostra a modificao na classe ListaContatosEndpoint, necessria para tratar a exceo no
momento de cadastramento do contato. O caminho feliz implementado no escopo try, enquanto
catch contm o cdigo que ser executado caso a exceo seja lanada.
ListaContatosEndpoint.java - try/catch
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response cadastrarContato(ContatoRep contato) {
try {
Contato contatoDominio = contato.converterParaDominio();
regrasContatos.cadastrar(contatoDominio);
ContatoRep contatoCadastrado = new ContatoRep(contatoDominio);
return Response.ok(contatoCadastrado).build();
}
catch (IdadeClienteException e) {
return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
}
}

O tratamento de excees no contexto de Web APIs implica em retornar uma resposta adequada ao
cliente, considerando a semntica do protocolo de comunicao utilizado. No casso do protocolo
HTTP, a exceo de idade mnima para o cadastro do contato resultado de uma informao
invlida proveniente do prprio cliente. Uma boa forma de informar ao cliente que ele est enviando
informaes invlidas atravs de uma resposta com o cdigo 400 (HTTP BAD REQUEST). Sendo
assim, aplicaes clientes de Web APIs devem ser capazes de interpretar corretamente os cdigos
retornados, resultando em sistemas mais robustos e confiveis aos usurios finais. Por fim, a figura a
seguir mostra a execuo de uma requisio contendo dados invlidos, e o resultado retornado pela
Web API.

44

Tratamento de Excees

Requisio com dados invlidos

Implementao de Providers
Tratar excees diretamente no endpoint pode tornar o cdigo pouco legvel e agradvel. Entretanto,
possvel utilizar Providers para mapear e tratar as excees lanadas durante a execuo da
aplicao. A classe IdadeContatoExceptionHandler mostra a implementao do Provider responsvel
por tratar as excees relacionadas idade do contato. Primeiramente, a classe precisa ser anotada
com @Provider. Alm disso, a classe deve implementar a interface ExceptionMapper para uma
determinada exceo ou hierarquia de excees. Por fim, a resposta adequada para a exceo
construda no mtodo toResponse. Por se tratar de uma configurao da Web API, os providers
foram agrupados no pacote config. Os providers ou seus pacotes devem ser registrados no arquivo
de configurao do Jersey, conforme mostrado em JerseyConfig.java. Dessa forma, o bloco try/catch
pode ser retirado sem prejuzo ao funcionamento da aplicao.

Tratamento de Excees

45

IdadeContatoException.java
@Provider
public class IdadeContatoExceptionHandler
implements ExceptionMapper<IdadeContatoException> {
@Override
public Response toResponse(IdadeContatoException exception) {
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}

JerseyConfig.java - registro de provider


@Component
@ApplicationPath("/agenda-api")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
this.register(RequestContextFilter.class);
this.packages("br.com.exemplo.agenda.api.endpoint");
this.register(IdadeContatoExceptionHandler.class);
}
}

Contato No Encontrado
Uma situao muito comum em Web APIs a inexistncia de um recurso solicitado pelo cliente. Ao
consultar ou solicitar alteraes de um contato inexistente, deve-se retornar uma resposta adequada
ao cliente. Para tratar este problema, vamos implementar uma classe de exceo para esta situao.
ContatoNaoEncontradoException.java
package br.com.exemplo.agenda.api.negocio;
public class ContatoNaoEncontradoException extends RuntimeException {
public ContatoNaoEncontradoException(String msg) {
super(msg);
}
}

Tratamento de Excees

46

A identificao de recursos inexistentes realizada diretamente na classe de manipulao do banco


de dados, uma vez que o Spring JDBC proporciona mecanismos para facilitar o tratamento de
excees. O arquivo JdbcContatoDao.java mostra o ponto de tratamento das excees quando um
contato no encontrado na base de dados. Quando uma instruo SQL executada atravs do
mtodo queryForObject, espera-se que um registro seja retornado, caso contrrio, ser lanada a
exceo do tipo IncorrectResultSizeDataAccessException. Dessa forma, o mtodo consultar trata esta
exceo e lana uma exceo de negcio em seu lugar. No caso da alterao e remoo, o nmero de
registros afetados por uma instruo SQL retornando pelo mtodo update. Dessa forma, possvel
verificar se a alterao ou remoo de um recurso foi realizada. Caso o nmero de registros afetados
seja zero, ser lanada uma exceo de contato no encontrado.
JdbcContatoDao.java - tratamento de recursos inexistentes
@Component
public class JdbcContatoDao implements ContatoDao {
@Override
public Contato consultar(String idContato) {
//codigo omitido
try {
return jdbcTemplate.queryForObject(... {// codigo omitido});
} catch (IncorrectResultSizeDataAccessException e) {
String msgErro = "Contato nao encontrado";
throw new ContatoNaoEncontradoException(msgErro);
}
}
@Override
public void alterar(Contato contato) {
//codigo omitido
int update = jdbcTemplate.update(sql.toString(), parametros);
if (update == 0) {
throw new ContatoNaoEncontradoException("Contato nao encontrado");
}
}
@Override
public void remover(String idContato) {
//codigo omitido
int removido = jdbcTemplate.update(sql.toString(), parametros);
if (removido == 0) {
String msgErro = "Contato nao encontrado";
throw new ContatoNaoEncontradoException(msgErro);
}
}
}

47

Tratamento de Excees

A classe ContatoNaoEncontradoExceptionHandler mostra a implementao do provider responsvel


por tratar as excees lanadas quando um contato no for encontrado na base de dados. O
provider retorna uma resposta com o cdigo HTTP 404 NOT FOUND, informando adequadamente
aplicao cliente que o recurso solicitado no existe. No se esquea de registrar este provider em
JerseyConfig.java.
ContatoNaoEncontradoExceptionHandler.java
@Provider
public class ContatoNaoEncontradoExceptionHandler
implements ExceptionMapper<ContatoNaoEncontradoException> {
@Override
public Response toResponse(ContatoNaoEncontradoException exception) {
return Response.status(Status.NOT_FOUND).entity(exception.getMessage()).build();
}
}

Requisio de um recurso inexistente

Aplicao Cliente da Web API


Com o objetivo de oferecer uma viso mais completa e prtica do uso de Web APIs, este captulo
apresenta os detalhes de desenvolvimento de uma aplicao cliente que interage com a Web API
desenvolvida no decorrer deste livro.

Viso Geral
De forma geral, Web APIs no so manipuladas diretamente pelos usurios finais, mas por
aplicaes intermedirias, conhecidas como aplicaes clientes. Estas aplicaes se comunicam
com uma ou mais Web APIs, e oferecem uma interface grfica adequada ao usurio final do
sistema. Existem diversas tecnologias para o desenvolvimento de clientes de Web APIs. Dentre
as mais comuns destacam-se as aplicaes desktop desenvolvidas com diferentes linguagens de
programao, aplicaes nativas ou hibridas para dispositivos mveis e aplicaes Web. Para este
exemplo, vamos desenvolver uma aplicao Web com HTML e JQuery com Ajax, pois uma opo
muito utilizada e relativamente simples de ser implementada.
Neste captulo vamos implementar uma aplicao cliente capaz de se comunicar com a Web API
de contatos que desenvolvemos anteriormente. A aplicao cliente constituda por quatro pginas
HTML. A partir da lista.html, que apresenta todos os contatos cadastrados, possvel cadastrar
novos contatos em cadastro.html, consultar todas as informaes de um contato especfico em
consulta.html ou modificar os dados em alteracao.html. A remoo de um contato no exige uma
pgina dedicada, sendo realizada diretamente na pgina da listagem.

Arquivos da aplicao cliente

48

49

Aplicao Cliente da Web API

Listagem dos Contatos


Para a aplicao cliente, a listagem significa consultar a Web API e apresentar as informaes
retornadas em uma pgina HTML. As informaes dos contatos podem ser facilmente apresentadas
em uma tabela. Alm de algumas propriedades, cada linha da tabela apresenta tambm controles
(links) para consultar mais informaes, alterar e remover.

Aplicao cliente - listagem dos contatos

No topo da pgina HTML existe um campo de texto que define a URL da Web API, alm dos botes
de listar e cadastrar. Embora especificar o endereo da Web API parea irrelevante inicialmente,
este mecanismo permite que uma nica aplicao cliente se comunique com diversas Web APIs
de agenda. Isto desejvel em cenrios onde existe um grande nvel de distribuio de dados, por
exemplo: departamentos, filiais ou parceiros que possuem sua prpria instncia da Web API, e os
dados precisam ser reunidos em uma aplicao cliente.
O arquivo lista.html apresenta o cdigo da pgina HTML necessrio para representar as informaes
dos contatos. As pginas HTML so capazes apenas de apresentar as informaes desejadas, a
interao com a Web API deve ser feita com javascript. Neste exemplo, vamos utilizar o apoio do
JQuery. Sendo assim, deve-se primeiro obter o arquivo JQuery e import-lo na pgina (linha 6).
Alm disso, o cdigo javascript responsvel pela interao com a Web API desenvolvido em um
arquivo separado (lista.js) e tambm importado pela pgina (linha 7).

https://jquery.com/download/

Aplicao Cliente da Web API

50

lista.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

<html>
<head>
<title>Gerenciador de Contatos</title>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="jquery/jquery-2.1.4.min.js"></script>
<script src="lista.js"></script>
</head>
<body>
<input id='apiPath' style='width: 400px;' value='http://localhost:8080/agenda-api'>
<button id='botaoListarTodos' type="button">Listar</button>
<button id='botaoCadastrar' type="button">Cadastrar</button>
<br><br>
<div id="listaDeContatos">
<table id="tabelaContatos" border="0" cellspacing="10">
<thead> <tr>
<th>Nome</th>
<th>Email</th>
<th>CPF</th>
<th></th>
<th></th>
<th></th>
</tr> </thead>
<tbody></tbody>
</table>
</div>
</body>
</html>

O arquivo lista.js - parte 1 mostra a implementao responsvel por realizar a consulta dos contatos
cadastrados na Web API. O arquivo comea com a funo listarTodosContatos, que realiza uma
requisio HTTP com ajax. Com base na URL da Web API, so criadas duas variveis, uma para
montar a URL da lista, e outra para criar a URL de acesso aos contatos (linhas 2 e 3). Entre as linhas
6 e 9 so definidos os detalhes da requisio ajax.
Basicamente duas coisas podem acontecer em uma requisio ajax com JQuery, sucesso ou erro. Em
caso de sucesso, ser atribuda variavel contatos a lista de registros retornados pela Web API, que
sero iterados e apresentados em uma linha da tabela HTML. Primeiramente, executada a funo
limparTabela (lista.js - parte2) para garantir que a tabela est vazia. Atravs do comando $.each,
todos os registros so iterados e transformados em uma nova linha, que adicionada tabela HTML.
As funcionalidades consultar, alterar e remover so disponibilizadas atravs de links associados com
cada linha da tabela. Funes so associadas aos links, que recebem a URL do contato e executam
as funcionalidades desejadas.

Aplicao Cliente da Web API

51

lista.js - parte 1
1
2
3

var listarTodosContatos = function() {


pathLista = $('#apiPath').val()+"/listaDeContatos";
pathContato = $('#apiPath').val()+"/contato?idContato=";

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

$.ajax({
url: pathLista,
type: 'GET',
async: true,
contentType: 'application/json',
success: function(contatos) {
limparTabela();
$.each(contatos, function(index, contato) {
var novaLinha =
'<tr>' +
'<td>' + contato.nome + '</td>'+
'<td>' + contato.email + '</td>'+
'<td>' + contato.cpf + '</td>'+
'<td><a href="#" onclick=consultar("'+pathContato+contato.id+'")>consultar</a></td>'+
'<td><a href="#" onclick=alterar("'+pathContato+contato.id+'")>alterar</a></td>'+
'<td><a href="#" onclick=remover("'+pathContato+contato.id+'")>remover</a></td>'+
'</tr>';
$("#tabelaContatos tr:last").after(novaLinha);
});
},
error: function() { }
});
};

As funes consultar e alterar recebem a URL do contato como parmetro de entrada, que armazenada na rea de memria local do navegador, denominada sessionStorage. Os itens armazenados
nesta rea de memria podem ser acessados mesmo quando ocorre troca de pgina. Dessa forma,
as funes de consulta e alterao direcionam o usurio para outras pginas e recuperam o item
armazenado na memria local do navegador. Mais adiante ser apresenta a recuperao do item. A
funo remover tambm recebe a URL do contato como parmetro para executar uma requisio
HTTP DELETE, que remove o contato na Web API. Note que a funcionalidade de remoo
implementada no mesmo arquivo javascript da listagem, uma vez que no exige uma pgina dedica.
Entretanto, mecanismos mais elaborados para remoo poderiam ser implementados, como por
exemplo a confirmao de remoo.

Aplicao Cliente da Web API

52

lista.js - parte 2
1
2
3

var limparTabela = function() {


$("#tabelaContatos").find("tr:gt(0)").remove();
}

4
5
6
7
8

var consultar = function(urlContato) {


sessionStorage.setItem("urlContato", urlContato);
window.location.href = "consulta.html";
}

9
10
11
12
13

var alterar = function(urlContato) {


sessionStorage.setItem("urlContato", urlContato);
window.location.href = "alteracao.html";
}

14
15
16
17
18
19
20
21
22
23
24

var remover = function(urlContato) {


$.ajax({
url: urlContato,
type: 'DELETE',
async: true,
success: function() {
listarTodosContatos();
}
});
}

25
26
27
28
29

$(document).ready(function() {
$("#botaoListarTodos").click(function() {
listarTodosContatos();
});

30
31
32
33
34

$("#botaoCadastrar").click(function() {
window.location.href = "cadastro.html";
});
});

O Problema de Cross Origin Request


Ao realizar o teste da listagem de contatos, nos deparamos com uma mensagem de erro. Isto
ocorre devido ao fato da pgina da listagem (file:// ) estar em um domnio diferente da Web
API (localhost:8080). Por motivos de segurana, navegadores restringem a execuo de requisies
entre diferentes domnios realizadas atravs de scripts. Para solucionar este problema, possvel

53

Aplicao Cliente da Web API

implementar o mecanismo de Cross-Origin Resource Sharing (CORS), que permite Web API
controlar o acesso de recursos de diferentes domnios. Dessa forma, deve-se implementar o CORS
diretamente na Web API. No pacote config do projeto da Web API, crie um provider de acordo com o
arquivo CorsInterceptor.java, que deve ser registrado em JerseyConfig.java. A partir deste momento,
a aplicao cliente deve ser capaz de interagir corretamente com a Web API.

Erro - Cross origin request


CorsInterceptor.java
package br.com.exemplo.agenda.api.config;
@Provider
public class CorsInterceptor implements ContainerResponseFilter {
private final Integer corsPreflightMaxAgeInSeconds = 30 * 60;
@Override
public void filter(ContainerRequestContext req, ContainerResponseContext resp)
throws IOException {
resp.getHeaders().add("Access-Control-Allow-Origin", req.getHeaderString("origin"));
resp.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
resp.getHeaders().add("Access-Control-Allow-Credentials", "true");
List<String> allowedHeaders = req.getHeaders().get("Access-Control-Request-Headers");
if (allowedHeaders != null) {
for (String allowedHeader : allowedHeaders) {
resp.getHeaders().add("Access-Control-Allow-Headers", allowedHeader);
}
}
resp.getHeaders().add("Access-Control-Max-Age", this.corsPreflightMaxAgeInSeconds);
}

54

Aplicao Cliente da Web API

Cadastro
O cadastro dos contatos realizado pela pgina cadastro.html e pelo arquivo cadastro.js. Primeiramente, so definidos os elementos HTML que compem a pgina, juntamente com algumas
instrues de estilo css que atuam na aparncia da pgina ( desejvel que o css seja implementado
em um arquivo distinto e importado na pgina html). Note que os campos de texto e seus rtulos
no esto inseridos em um formulrio HTML, como ocorre em aplicaes tradicionais. Uma vez
que o JQuery o responsvel pela realizao da requisio via ajax, nenhum formulrio precisa ser
submetido. As informaes escritas nos campos de texto so obtidas com base no identificador dos
elementos. Estas informaes so utilizadas para construir um documento JSON que enviado para
a Web API para o cadastro de um novo contato.

Aplicao cliente - cadastro de contato

Aplicao Cliente da Web API

cadastro.html
<html>
<head>
<title>Gerenciador de Contatos</title>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script src="jquery/jquery-2.1.4.min.js"></script>
<script src="cadastro.js"></script>
<style>
label {
float: left;
width: 120px;
clear: both;
margin: 2px;
}
input {
width: 250px;
clear: both;
margin: 2px;
}
</style>
</head>
<body>
<h1>Cadastro de Contato</h1>
<div id="dadosContato">
<h3>Dados do contato</h3>
<label>Nome:</label><input id="nome" type="text"></input><br>
<label>Email:</label><input id="email" type="text"></input><br>
<label>CPF:</label><input id="cpf" type="text"></input><br>
<label>Telefone:</label><input id="telefone" type="text"></input><br>
<label>Data Nascimento:</label><input id="datan" type="text"></input><br>
<h3>Endereo</h3>
<label>Estado:</label><input id="estado" type="text"></input><br>
<label>Cidade:</label><input id="cidade" type="text"></input><br>
<label>Bairro:</label><input id="bairro" type="text"></input><br>
<label>Logradouro:</label><input id="logradouro" type="text"></input><br><br>
<button id="botaoCadastrar">Cadastrar</button><br><br>
<div id="resultado"></div>
</div>
</body>
</html>

55

Aplicao Cliente da Web API

56

O arquivo cadastro.js implementa a funcionalidade para o cadastramento do contato. A funo


cadastrar obtm as informaes inseridas pelo usurio e constri o objeto dadosContato com os
dados obtidos. Em seguida, implementado o cdigo que realiza uma requisio HTTP POST para a
URL da Web API, contento a representao JSON do documento dadosContato. A funo cadastrar
invocada quando o usurio pressionar boto *botaoCadastrar. Ao final da requisio, apresentado
ao usurio uma mensagem de sucesso ou erro de acordo com a resposta da Web API.
cadastro.js
var cadastrar = function() {
var dadosContato = {
nome: $("#nome").val(),
email: $("#email").val(),
cpf: $("#cpf").val(),
telefone: $("#telefone").val(),
dataNascimento: $("#datan").val(),
estado: $("#estado").val(),
cidade: $("#cidade").val(),
bairro: $("#bairro").val(),
logradouro: $("#logradouro").val()
};
$.ajax({
url: "http://localhost:8080/agenda-api/listaDeContatos",
type: 'POST',
async: true,
contentType: 'application/json',
data: JSON.stringify(dadosContato),
success: function() {
$("#resultado").empty();
$("#resultado").append("Contato cadastrado com sucesso")
},
error: function(xhr, status, error) {
$("#resultado").empty();
$("#resultado").append("Erro ao cadastrar: " + xhr.responseText)
}
});
};
$(document).ready(function() {
$("#botaoCadastrar").click(function() {
cadastrar();
});
});

57

Aplicao Cliente da Web API

Consulta
A consulta busca as informaes de um contato especfico e apresenta na pgina consulta.html. O
arquivo consulta.js implementa a requisio ajax que busca as informaes na Web API. Note que
no momento que pgina est pronta (contexto $(document).ready()), a funo consultarContato
invocada com a URL do contato como parmetro de entrada. Entretanto, a URL obtida a partir
do sessionStorage, armazenada anteriormente pela pgina de listagem. Essa uma forma de tornar
acessveis as informaes que precisam ser acessadas entre troca de pginas. Uma vez que a funo
consultarContato obtm os dados da Web API, as informaes so associadas aos elementos HTML
de acordo com seus identificadores.

Aplicao cliente - consulta de um contato especfico


consulta.html
1
2
3
4
5
6
7
8
9
10
11
12

<html>
<head>
<title>Gerenciador de Clientes</title>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="jquery/jquery-2.1.4.min.js"></script>
<script src="consulta.js"></script>
</head>
<body>
<h1>Detalhes do Contato</h1>
<h3>Dados do contato</h3>
<b>Nome:</b><span id="nome"></span><br>

Aplicao Cliente da Web API


13
14
15
16
17
18
19
20
21
22
23

<b>Email:</b><span id="email"></span><br>
<b>CPF:</b><span id="cpf"></span><br>
<b>Telefone:</b><span id="telefone"></span><br>
<b>Data Nascimento:</b><span id="datan"></span><br>
<h3>Endereco</h3>
<b>Estado:</b><span id="estado"></span><br>
<b>Cidade:</b><span id="cidade"> </span><br>
<b>Bairro:</b><span id="bairro"> </span><br>
<b>Logradouro:</b><span id="logradouro"></span><br>
</body>
</html>

consulta.js
var consultarContato = function(urlContato) {
$.ajax({
url: urlContato,
type: 'GET',
async: true,
contentType: 'application/json',
success: function(contato) {
$("#nome").text(contato.nome);
$("#email").text(contato.email);
$("#cpf").text(contato.cpf);
$("#telefone").text(contato.telefone);
$("#datan").text(contato.dataNascimento);
$("#estado").text(contato.estado);
$("#cidade").text(contato.cidade);
$("#bairro").text(contato.bairro);
$("#logradouro").text(contato.logradouro)
},
error: function() { }
});
};
$(document).ready(function() {
consultarContato(sessionStorage.getItem('urlContato'));
});

58

59

Aplicao Cliente da Web API

Alterao
A alterao do contato realizada pela pgina alterao.html, que apresenta rtulos e campos
de texto para que as informaes de um contato sejam alteradas. No arquivo alteracao.js, duas
funes so implementadas: consultarContato e consultarContato. A funo de consulta a mesma
do exemplo anterior, que busca as informaes do contato desejado para que suas informaes
sejam apresentadas nos campos de texto. Esta funcionalidade poderia ser reutilizada com algumas
modificaes no arquivo consulta.js, entretanto, para facilitar o entendimento, permitimos algumas
duplicaes de cdigo. Quando o boto com o identificador botaoAlterar pressionado, a funo
alterar invocada. Esta funo obtm os dados dos campos de texto, constri um objeto JSON e
realiza uma requisio HTTP PUT para a URL do contato armazenada no sessionStorage.

Aplicao cliente - alterao dos dados do contato

Aplicao Cliente da Web API

alteracao.html
<html>
<head>
<title>Gerenciador de Contatos</title>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="jquery/jquery-2.1.4.min.js"></script>
<script src="alteracao.js"></script>
<style>
label {
float: left;
width: 120px;
clear: both;
margin: 2px;
}
input {
width: 250px;
clear: both;
margin: 2px;
}
</style>
</head>
<body>
<h1>Alterao de Contato</h1>
<div id="dadosContato">
<h3>Dados do contato</h3>
<label>Nome:</label> <input id="nome" type="text"></input> <br>
<label>Email:</label> <input id="email" type="text"></input> <br>
<label>CPF:</label> <input id="cpf" type="text"></input> <br>
<label>Telefone:</label> <input id="telefone" type="text"></input> <br>
<label>Data Nascimento:</label> <input id="datan" type="text"></input>
<h3>Endereo</h3>
<label>Estado:</label> <input id="estado" type="text"></input> <br>
<label>Cidade:</label> <input id="cidade" type="text"></input> <br>
<label>Bairro:</label> <input id="bairro" type="text"></input> <br>
<label>Logradouro:</label> <input id="logradouro" type="text"></input><br><br>
<button id="botaoAlterar">Alterar</button> <br><br>
<div id="resultado"></div>
</div>
</body>
</html>

60

Aplicao Cliente da Web API

alteracao.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var consultarContato = function(urlContato) {


$.ajax({
url: urlContato,
type: 'GET',
async: true,
contentType: 'application/json',
success: function(contato) {
$("#nome").val(contato.nome);
$("#email").val(contato.email);
$("#cpf").val(contato.cpf);
$("#telefone").val(contato.telefone);
$("#datan").val(contato.dataNascimento);
$("#estado").val(contato.estado);
$("#cidade").val(contato.cidade);
$("#bairro").val(contato.bairro);
$("#logradouro").val(contato.logradouro)
},
error: function() {}
});
};

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

var alterar = function(urlContato) {


var dadosContato = {
nome: $("#nome").val(),
email: $("#email").val(),
cpf: $("#cpf").val(),
telefone: $("#telefone").val(),
dataNascimento: $("#datan").val(),
estado: $("#estado").val(),
cidade: $("#cidade").val(),
bairro: $("#bairro").val(),
logradouro: $("#logradouro").val()
};
$.ajax({
url: urlContato,
type: 'PUT',
async: true,
contentType: 'application/json',
data: JSON.stringify(dadosContato),
success: function() {
$("#resultado").empty();
$("#resultado").append("Contato alterado com sucesso")
},

61

Aplicao Cliente da Web API


44
45
46
47
48
49
50
51
52
53
54
55

error: function(xhr, status, error) {


$("#resultado").empty();
$("#resultado").append("Erro ao alterar: " + xhr.responseText)
}
});
};
$(document).ready(function() {
consultarContato(sessionStorage.getItem('urlContato'));
$("#botaoAlterar").click(function() {
alterar(sessionStorage.getItem('urlContato'));
});
});

62

Construo e Implantao do Projeto

63

Prximos Passos

64

Vous aimerez peut-être aussi