└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # Criando uma API Rest + Banco de dados
2 |
3 | Olá! Neste tutorial pretendo explicar os pontos básicos sobre a criação
4 | de uma api REST utilizando o framework SpringBoot, assim como a integração
5 | com um Banco de Dados (Será utilizado o H2, falarei sobre).
6 |
7 | Cada um dos pontos será explicado de forma superficial, de forma que seja passado
8 | apenas o necessário para se entender como todo o sistema funciona. No tutorial
9 | será utilizada a IDE Eclipse Mars, com Java 8 e SpringBoot.
10 |
11 | Recomendo a utilização do **Postman**, que pode ser encontrado na loja de apps do Chrome,
12 | mas que roda independente do navegador. O Postman é uma ferramenta que vai nos ajudar
13 | a testar a API, realizando as requisições. O Postman será utilizado nos exemplos.
14 |
15 | Viu algum erro ou tem uma sugestão? Faça um pull-request! :D
16 |
17 | # Iniciando - Maven e POM.xml
18 |
19 | Inicialmente, você deve criar um projeto Maven e adicionar o SpringBoot como dependência,
20 | junto do módulo **starter-web**. Isso pode ser feito através deste [site](https://start.spring.io),
21 | basta adicionar a dependência e baixar o zip.
22 |
23 | Será gerado um projeto em branco, apenas com uma classe Main e uma estrutura de pacotes
24 | inicial. Você deve importar o projeto Maven em sua IDE.
25 |
26 | Verifique a classe gerada está anotada com **@SpringBootApplication**, isto faz com que o Spring
27 | identifique que esse é o ponto de início da aplicação, a classe também possui um método main,
28 | ele que será executado para a aplicação iniciar.
29 |
30 | # DevTools - Uma mão na roda
31 |
32 | Para desenvolvimento do tutorial, e, posteriormente, sua aplicação, o uso do DevTools
33 | se faz quase que obrigatório (quase, podemos fazer sem, mas tendo mais trabalho). O
34 | DevTools é uma ferramenta que, sempre que o código é modificado, re-executa a aplicação,
35 | para as modificações inseridas serem aplicadas. Isso pode ser feito manualmente, mas, é bem chato,
36 | então sugiro utilizar o DevTools.
37 |
38 | Para o utilizar, basta adicionar como dependência no pom.xml, da seguinte forma:
39 |
40 | ```
41 |
42 | org.springframework.boot
43 | spring-boot-devtools
44 | runtime
45 |
46 | ```
47 |
48 | Obs: Se você não estiver utilizando o DevTools, sempre pare a execução atual da aplicação
49 | antes de iniciar uma nova, pois apenas iniciar uma nova execução faz com que a última não seja
50 | terminada, fazendo com que a porta continue sendo utilizada, não permitindo que a nova execução
51 | tenha sucesso. Este erro se parece com:
52 |
53 | ```
54 | Description:
55 |
56 | The Tomcat connector configured to listen on port 8080 failed to start. The port may already be in use or the connector may be misconfigured.
57 | ```
58 |
59 | Se isso acontecer, basta entrar no gerenciador de processos (windows) ou fazer **ps aux | grep java** (linux)
60 | e matar o processo Java.
61 |
62 | # Criando a API
63 | ## Criando nosso primeiro endpoint - Hello Word
64 |
65 | Agora que já temos a base para aplicação pronta, podemos começar a fazer o que realmente interessa, a API Rest.
66 | Se você não sabe o que é uma API Rest, recomendo ler [isso](https://pt.stackoverflow.com/questions/45783/o-que-%C3%A9-rest-e-restful),
67 | e depois continuar.
68 |
69 | Seu próximo passo é criar uma classe, no exemplo vou utilizar a classe *InterfaceRest.java*,
70 | devemos anotar a classe com **@RestController**. Esta anotação faz o Spring identificar a classe como
71 | um controlador Rest, que podemos adicionar enpoints para nossas funcionalidades.
72 |
73 | ```
74 | @RestController
75 | public class InterfaceRest {
76 | }
77 | ```
78 |
79 | Dentro da classe vamos criar um método chamado helloWord, que retorna String e não tem parâmetros.
80 | O método deve ser anotado com **@RequestMapping(value = "/teste", method = RequestMethod.GET)**.
81 | A anotação **@RequestMapping** faz com que o método seja relacionado à um endpoint, ou,
82 | ao fazermos uma requisição no endpoint *"/teste"* o método seja executado. O parâmetro **value** da anotação
83 | define qual endpoint (como String) o método está relacionado.
84 |
85 | Na anotação nós temos outro parâmetro, o **method**, que indica qual método HTTP (O tipo da requisição)
86 | o endpoint e o método Java está relacionado. Logo, um método java está associado à um endpoint e um tipo
87 | de requisição. No nosso caso, o método vai aceitar requisições de **GET** para o endpoint de **"/teste"**.
88 |
89 | No corpo do método você deve fazer apenas ele retornar uma String qualquer, ex: "Oi, eu estou funcionando!".
90 | Agora, execute a aplicação, abra o Postman e realize a requisição para **localhost:8080/teste**, verifique que
91 | o retorno da response foi o que você adicionou no método Java.
92 |
93 | ```
94 | @RestController
95 | public class InterfaceRest {
96 |
97 | @RequestMapping(value="/teste", method=RequestMethod.GET)
98 | public String helloWord() {
99 | return "Oi, eu estou funcionando!";
100 | }
101 | }
102 | ```
103 |
104 | Obs: Todas as classes para o projeto devem estar no mesmo package ou em packages filhos do que está a classe
105 | Main, caso contrário, o Spring não reconhecerá as classes para adicionar seus recursos corretamente à execução.
106 |
107 | ## Recursos? - Responsabilidades diferentes
108 |
109 | Agora já podemos criar endpoints que retornam informações através de uma interface Rest, mas podemos
110 | fazer isso de uma forma mais bonita do que vimos até agora, que é separando cada classe **RestController**
111 | por recurso, ex: UsuarioRest, SerieRest, DocumentoRest, fazendo com que nossa aplicação tenha endpoints do tipo
112 |
113 | ```
114 | /usuario/cadastrar - Cadstrar um usuário - POST
115 | /usuario/regibalbo - Recuperar informações para o usuário regibalbo - GET
116 | /serie/1 - Recuoperar informações da série 1.
117 | ```
118 |
119 | Isso poderia ser feito utilizando o caminho completo em cada método, da mesma forma que já vimos,
120 | mas também podemos realizar uma padronização, separando cada classe para um recurso, e seus métodos
121 | apenas terem o fim do endpoint para o serviço. Isso pode ser feito adicionando a anotação **@RequestMapping**
122 | na própria classe, e definindo um **value** que será a "base" para os endpoints definidos nos métodos da classe.
123 | Ex:
124 |
125 | ```
126 | @RestController
127 | @RequestMapping(value = "/usuario")
128 | public class Teste {
129 |
130 | @RequestMapping(value = "/consulta/{nome}", method = RequestMethod.GET)
131 | public String consultarUsuario(@PathVariable String nome) {
132 | return nome;
133 | }
134 | }
135 | ```
136 |
137 | Isso vai fazer com que o método java consultarUsuario seja executado ao realizar uma requisição de POST
138 | em **/usuario/consulta/eric**, por exemplo.
139 |
140 | **@PathVariable**: Anota um parâmetro do método que está relacionado à um endpoint. Os endpoints podem ter variáveis, ou,
141 | pedaços não fixos para a requisição, como no exemplo, utilizamos "/consulta/**{nome}**", onde a variável
142 | é definida dentro de {}, e recuperada no método se identificando o parâmetro com @PathVariable. Se realizarmos
143 | uma requisição **GET** em /usuario/consulta/eric, veremos que no método, a variável *nome* vai ter valor "eric". Teste isso.
144 | Você pode fazer o método retornar uma String, sendo essa o parâmetro, para fins de debug.
145 |
146 | ## Enviando informações para nossa API - POST, PUT
147 |
148 | Agora já sabemos como criar endpoints e os separar por recursos, vamos ver como podemos adicionar métodos que recebem
149 | **objetos** através das requisições, utilizando métodos http de POST e PUT.
150 |
151 | Primeiro, precisamos definir um objeto para ser retornado, crie uma classe qualquer, que contenha um campo de String e um
152 | inteiro, adicione gets e sets para cada campo.
153 | Obs: É necessário que os campos tenham os gets e sets.
154 |
155 | ```
156 | public class ObjetoTeste {
157 | private String nome;
158 | private int quantidade;
159 |
160 | public String getNome() {
161 | return nome;
162 | }
163 | public void setNome(String nome) {
164 | this.nome = nome;
165 | }
166 | /**/
167 | }
168 | ```
169 |
170 | Uma vez que você criou sua primeira classe, implemente um método com tipo GET que retorna algum objeto
171 | que você criou.
172 |
173 | ```
174 | @RequestMapping(value="/teste", method=RequestMethod.GET)
175 | public ObjetoTeste testeRecuperar() {
176 | ObjetoTeste obj = new ObjetoTeste();
177 | obj.setNome("clemison");
178 | obj.setQuantidade(12382);
179 | return obj;
180 | }
181 | ```
182 |
183 | Agora, faça uma requisição e verifique como o objeto foi retornado, seguindo o padrão JSON, e que
184 | cada chave representa o nome do atributo para o objeto. Você vai enviar os dados para o servidor
185 | neste mesmo formato.
186 |
187 | ## Realizando o POST
188 |
189 | Agora que já sabemos como a interface Rest trata os dados, e como podemos retornar objetos completos
190 | por vez, vamos ver como fazer a api receber objetos.
191 |
192 | Crie um método com tipo POST, pode utilizar o mesmo endpoint*.
193 |
194 | * O mesmo endpoint pode suportar diversos tipos de requisição, o método java que vai ser executado fica
195 | de acordo com o tipo da requisição e o tipo definido em sua anotação.
196 |
197 | ```
198 | @RequestMapping(value="/teste", method=RequestMethod.POST)
199 | public String testeReceber(@RequestBody ObjetoTeste objetoRecebido) {
200 | return "Recebi um objeto com " + objetoRecebido.getNome() + " - e quantidade " + objetoRecebido.getQuantidade();
201 | }
202 | ```
203 |
204 | A anotação **@RequestBody** identifica que o corpo da requisição deve ser transformado no objeto
205 | anotado pela mesma.
206 |
207 | No Postman, você deve mudar o tipo da requisição para POST, ir para a opção de **body**, marcar a opção **raw**
208 | e depois definir o tipo para **JSON**. Defina o body a ser enviado com um JSON no formato do seu objeto. Para
209 | o caso do exemplo:
210 |
211 | ```
212 | {
213 | "nome": "clebinho",
214 | "quantidade": "197"
215 | }
216 | ```
217 |
218 | Agora, faça a requisição e veja que foi retornada uma String com as informações do objeto. Criar um método de PUT
219 | funciona da mesma forma, os dois têm apenas a diferença semantica de que POST é utilizado para inserção/cadastro
220 | e o PUT é utilizado para atualização.
221 |
222 | Agora já sabemos como:
223 | * Fazer consultas de informações, com GET, e passando parâmetros opcionais no endpoint.
224 | * Enviar informações para nossa API, com POST e PUT.
225 |
226 | ## Testes - O que mais podemos fazer?
227 |
228 | Agora você pode fazer testes para verificar a API funcionando e descobrir o que mais podemos fazer, implemente o seguinte:
229 | * Um método de GET retornar uma **lista** (ou array) de objetos
230 | * Um método POST guardar o objeto enviado em um *Map*, que seja um campo estático, e depois em um GET
231 | recuperar esse objeto do *Map* (simulando persistência). Você pode guardar os objetos no Map com as chaves
232 | sendo **nome**, e recuperar através do GET passando o nome em seu endpoints, como mostrado anteriormente.
233 | * Um método de PUT que atualiza um objeto no Map, e em seguida um GET para recuperar o objeto atualizado.
234 |
235 | # Integrando com Banco de Dados H2 - Agora as coisas ficam interessantes
236 |
237 | ## H2?
238 | H2 é um banco de dados em memória, ou seja, ele simula um BD, mas sempre que a aplicação é reiniciada os dados
239 | são perdidos, diferente de um BD real, como MySQL. A vantagem de se utilizar H2 é que não precisaremos configurar
240 | um BD localmente para a máquina, nem criar tabelas e colunas.
241 |
242 | ## Driver para conexão
243 | Para se comunicar com o BD utizaremos drivers, que são interfaces que convertem os comandos Java para comandos
244 | de banco de dados. O driver que mostrarei aqui segue a especificação do JPA (Java Persistence API), que torna fácil
245 | a migração para outro BD, sem precisarmos mudar o código, apenas alterando o driver que vai ser utilizado, desde que siga
246 | o mesmo padrão.
247 |
248 | ## Adicionando JPA
249 | Adicione como dependência do pom a biblioteca do JPA:
250 |
251 | ```
252 |
253 | org.springframework.boot
254 | spring-boot-starter-data-jpa
255 |
256 | ```
257 |
258 | ## Adicionando driver do H2
259 | Para adicionar o driver do H2, você deve adicionar como dependência do pom:
260 |
261 | ```
262 |
263 | com.h2database
264 | h2
265 |
266 | ```
267 |
268 | ## Hibernate
269 | Para realizar as operações de consultar, guardar e atualizar informações, vamos utilizar o framework Hibernate,
270 | ele se comunicará com o driver do H2 de forma automática. O Hibernate é um framework robusto, que contém diversas
271 | funcionalidades e uma vasta aplicação e usabilidade, mas aqui abordaremos apenas o básico para uma aplicação o utilizar.
272 |
273 | Adicione as configurações necessárias para o Hibernate, no arquivo **/src/main/resources/application.properties**:
274 |
275 | ```
276 | spring.datasource.url = jdbc:h2:file:~/h2/app_db;DB_CLOSE_ON_EXIT=FALSE
277 | spring.datasource.username = sa
278 | spring.datasource.password =
279 | spring.datasource.driverClassName = org.h2.Driver
280 | spring.jpa.hibernate.ddl-auto = update
281 |
282 | logging.level.org.hibernate.SQL=INFO
283 | logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
284 | ```
285 |
286 | ## Camada de comunicação com o BD - Os DAO's
287 | Para a comunicação com o BD vamos utilizar uma camada específica, que chamaremos de DAO (Data Access Object),
288 | ela realizará todas as operações com o BD.
289 |
290 | Crie um DAO, no exemplo utilizarei o TesteDAO. Sua classe DAO deve estar anotada com **@Repository**, indicando que
291 | esta classe é um classe Repositório(de comunicação com o BD). Você também deve adicionar no DAO a anotação **@Transactional**,
292 | que vai fazer com que as operações (execuções do métodos da classe) sejam tratadas da forma de [transações](https://pt.wikipedia.org/wiki/Transa%C3%A7%C3%A3o_em_base_de_dados).
293 | Dentro de sua classe DAO você deve *injetar* o objeto do Hibernate que vai tratar as operações, da seguinte forma:
294 |
295 | ```
296 | @Transactional
297 | @Repository
298 | public class TesteDAO {
299 | @PersistenceContext
300 | private EntityManager em;
301 | }
302 | ```
303 |
304 | Veja sobre Injeção de dependências [aqui](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html).
305 | A anotação **@PersistenceContext** vai adicionar (automaticamente) uma instância de EntityManager na classe, assim que ela for iniciada.
306 | O EntityManager (Gerenciador de entidades) que vai tratar as operações.
307 |
308 | Os principais métodos do EntityManager que precisamos conhecer são:
309 | * Persist(entidade) - Vai inserir uma entidade no BD
310 | * Merge(entidade) - Vai atualizar uma entidade no BD
311 | * CreateTypedQuery(String, TipoObjeto) - Cria consultas por Objetos.
312 |
313 | Agora, adicione um método na classe DAO para persistir seu *ObjetoTeste*, da seguinte forma:
314 |
315 | ```
316 | public ObjetoTeste persisteObjeto(ObjetoTeste obj) {
317 | em.persist(obj);
318 | return obj;
319 | }
320 | ```
321 |
322 | E um para recuperar do BD, aqui criaremos uma Query:
323 |
324 | ```
325 | public ObjetoTeste consultaObjeto(Long id) {
326 | TypedQuery query = em.createTypedQuery("select obj from ObjetoTeste obj where obj.id = :id");
327 | query.setParameter("id", id);
328 | return query.getSingleResult();
329 | }
330 | ```
331 |
332 | Note que a sintaxe da query, que é estruturada em [HQL](https://docs.jboss.org/hibernate/orm/3.5/reference/pt-BR/html/queryhql.html)(Hibernate Query Language) é parecida com a sintaxe de SQL, mas simplifica a query em alguns casos. O método **getSingleResult**
333 | vai nos retornar o objeto de resultado da consulta, de acordo com o ID passado (falarei sobre ID em seguida).
334 |
335 | Uma das diferenças do SQL se dá por não consultarmos por tabelas, mas sim por tipos de objetos, como no exemplo,
336 | estamos pegando dentre ObjetoTeste, o Hibernate consegue distinguir o que estamos buscando por os objetos
337 | persistidos terem a classe anotada por **@Entity**.
338 |
339 | Você pode adicionar parâmetros para a busca utilizando **:nomePropriedade**, e realizando a comparação
340 | como mostrado na query de exemplo acima. Para substituir os parâmetros, deve-se utilizar o método **query.setParameter**.
341 |
342 | Também é possível realizar uma consulta por outros parâmetros, seguindo o ex:
343 |
344 | ```
345 | public ObjetoTeste consultaObjeto(Long id, String nomeObjeto) {
346 | TypedQuery query = em.createTypedQuery("select obj from ObjetoTeste obj where obj.id = :id and obj.nomeObjeto = :nomeObjeto", ObjetoTeste.class);
347 | query.setParameter("id", id);
348 | query.setParameter("nomeObjeto", nomeObjeto);
349 | return query.getSingleResult();
350 | }
351 | ```
352 |
353 | Deve-se apenas ter cuidado ao fazer consultas que não sejam por id porque essas podem retornar mais de um objeto,
354 | enquanto por id sempre retornará apenas 1, ou nenhum, caso o objeto não exista.
355 |
356 | ## A entidade que vai ser persistida
357 | * Lembre-se de sempre criar gets e sets para as propriedades dos objetos a serem persistidos.
358 |
359 | Para podermos persistir uma entidade, ela precisa ser devidamente identificada e distinguível de outras do mesmo
360 | tipo, fazemos isso utilizando as anotações do próprio JPA, seguindo:
361 |
362 | * @Entity - Anota a classe, identifica que esse objeto pode ser persistido no BD
363 | * @Column - Anota uma propriedade, identifica que a propriedade vai ser uma coluna do BD
364 | * @Id - Anota uma propriedade, diz que essa propriedade será o identificador para a entidade, utilizaremos identificadores do tipo Long.
365 |
366 | Na anotação @Column podemos ainda adicionar qual nome específico da Coluna e regras, como de a propriedade não poder
367 | ser nula, ex:
368 |
369 | ```
370 | @Column(name = "nome_usuario", nullable = false)
371 | private String nomeUsuario;
372 | ```
373 |
374 | Isso vai fazer com que não seja permitido inserir a propriedade nomeUsuario com valor *null*, assim como
375 | a propriedade será identificada no BD pela coluna **nome_usuario**.
376 |
377 | Adicione no seu objeto um campo id do tipo Long, e o anote da seguinte forma:
378 |
379 | ```
380 | @Id
381 | @GeneratedValue(strategy = GenerationType.IDENTITY)
382 | private Long id;
383 | ```
384 |
385 | Como mencionado acima, a anotação **@Id** vai fazer com o que campo seja o elemento que vai distinguir dois objetos para
386 | o mesmo tipo. A anotação **@GeneratedValue** vai fazer com que ele seja gerado automaticamente, e não precisemos
387 | nos preocupar em guardar dois objetos do mesmo tipo com um mesmo id. Todas as entidades que forem ser persistidas
388 | devem ter um ID.
389 |
390 | Feito isso, anote outras propriedades que deseja guardar no BD, da mesma forma que o exemplo.
391 |
392 | ## Usando o DAO
393 | Para utilizar o DAO para se recuperar e persistir os objetos, vamos utilizar a injeção de dependências, como já
394 | mencionado anteriormente, da seguinte forma:
395 |
396 | ```
397 | @Autowired
398 | private TesteDAO dao;
399 | ```
400 |
401 | Você deve adicionar esta injeção na sua classe Rest. Agora, faça com que algum método de POST receba o ObjetoTeste,
402 | o persista no BD, e em seguida o de GET recupere do BD, passando o id do objeto persistido. Lembre de retornar o objeto
403 | persistido no POST, para que você possa saber qual o id para a consulta.
404 |
405 | Exemplo para os endpoints de POST e GET:
406 |
407 | ```
408 | @RequestMapping(value = "/teste/{id}", method = RequestMethod.GET)
409 | public ObjetoTeste teste(@PathVariable Long id) {
410 | return dao.consultar(id);
411 | }
412 |
413 | @RequestMapping(value = "/teste", method = RequestMethod.POST)
414 | public ObjetoTeste testeReceber(@RequestBody ObjetoTeste objetoRecebido) {
415 | return dao.inserir(objetoRecebido);
416 | }
417 | ```
418 |
419 | * Para realizar a consulta por ID, utilize as variáveis de parâmetro do endpoint, e passe a variável para o método de consulta do DAO.
420 | * Adicione no DAO um método que atualiza uma entidade, este método vai utilizar o **EntityManager.merge**.
421 | * Adicione um método do tipo PUT que vai atualizar uma entidade no BD, lembre que a entidade passada deve ter o ID.
422 |
423 | ## Relacionando objetos
424 | E se nós quisermos dizer que o usuário vai ter ObjetoTeste? Ou uma lista de objetos? Precisamos saber como relacionar os objetos agora,
425 |
426 | Existem algumas formas de fazer isso, aqui apresentarei o formato de relação inversa, onde um usuário vai ter uma lista
427 | de objetos. Para isso, faremos o nosso ObjetoTeste ter uma propriedade chamada **idUsuario**, e sempre persistiremos
428 | o objeto com esta propriedade sendo o id de um usuário do sistema, e para a consulta nós buscaremos por *todas as séries que pertencem a este usuário*.
429 |
430 | Nosso objeto terá um novo campo:
431 |
432 | ```
433 | @Column(name="idUsuario")
434 | private Long idUsuario;
435 | ```
436 |
437 | E se quisermos consultar todos os objetos de um usuário, podemos fazer isso com um método no DAO desta forma:
438 |
439 | ```
440 | public List consultarObjetosDoUsuario(Long idUsuario) {
441 | TypedQuery query = CreateTypedQuery("select obj from ObjetoTeste obj where obj.idUsuario = :idUsuario", ObjetoTeste.class)
442 | query.setParameter("idUsuario", idUsuario);
443 | return query.getResultList();
444 | }
445 | ```
446 |
447 | O método getResultList vai nos retornar uma lista de objetos, onde todos tem o idUsuario igual ao passado por parâmetro,
448 | ou seja, no dá todas os objetos do usuário.
449 |
450 | ## Finalizando
451 | Pronto, agora você já tem configurado no seu projeto o H2, utilizando o Hibernate, a classe Rest que se comunica com o DAO
452 | e realiza as operações com o BD, servidor finalizado!
453 |
454 | # Integrando com a parte Web - Servindo arquivos estáticos
455 |
456 | Agora que já temos nossa API funcionando com o H2, vamos fazer com que nosso servidor possa
457 | enviar os arquivos estáticos da parte Web de nosso projeto.
458 |
459 | Primeiro, você deve adicionar seus arquivos Web na pasta **src/main/webapp**. Para o tutorial, vamos adicionar
460 | apenas um arquivo **index.html** com o seguinte:
461 |
462 | ```
463 | Olá, estou funcionando!
464 | ```
465 |
466 | Agora, vamos precisar adicionar uma nova classe para a configuração da forma que os arquivos
467 | estáticos serão servidos. Crie uma classe de configuração que herde de **WebMvcConfigurerAdapter**
468 | e sobrescreva o método **addResourceHandlers** da seguinte forma:
469 |
470 | ```
471 | @Configuration
472 | public class Config extends WebMvcConfigurerAdapter {
473 |
474 | @Override
475 | public void addResourceHandlers(ResourceHandlerRegistry registry) {
476 | registry.addResourceHandler("/*")
477 | .addResourceLocations("/*");
478 | }
479 | }
480 | ```
481 |
482 | Execute a aplicação e, através do navegador, entre no link **localhost:8080**, veja que seu conteúdo html
483 | agora está sendo servido com o próprio Spring.
484 |
485 | Com o **addResourceHandler**, nós fazemos com que ao se tente realizar uma requisição em qualquer ponto
486 | em **/** ele tente servir algum arquivo estático, caso exista, tentando recuperar o arquivo a partir
487 | da pasta **src/main/webapp**.
488 |
489 | Perceba que não precisamos colocar o **/index.html** especificamente na url, por o spring tentar sempre
490 | achar algum arquivo index.html para a pasta, e então o enviar. Teste adicionar outro arquivo html e o ver
491 | no navegador, mudando o final da url.
492 |
493 | Dessa forma, já podemos servir todos os arquivos que precisemos. Teste adicionar um arquivo JS e o importar no html,
494 | assim como adicionar subpastas e as acessar a partir do navegador.
495 |
496 | ## Realizando requisições do cliente para a API
497 |
498 | Para a parte Web se comunicar a sua API, basta realizar as requisições diretamente nos endpoints, sem
499 | precisar adicionar o endereço completo (como fazíamos no Postman). :D
500 |
--------------------------------------------------------------------------------