Neste post daremos continuidade a uma série de postagens sobre Web-Services divididos entre conceitos teóricos e praticos. Nos 3 primeiros posts focou-se em teoria com um sobre Web Services, outro sobre RESTful Web Services e mais um sobre os HTTP Status Codes mais comuns. Este post por sua vez complementa o que foi desenvolvido no post Do zero ao REST em 5 minutos com SpringBoot. Então antes de começar veja o post anterior ou baixe o código e vamos entender na prática como funcionam os verbos REST.
Agora faremos algumas alterações no projeto desenvolvido no post anterior. Para começar crie os seguintes pacotes br.com.erudio.models, br.com.erudio.services, br.com.erudio.services.implementations e br.com.erudio.web.controllers. Depois mova a classe Greeting para o pacote br.com.erudio.models e a classe GreetingController para o pacote br.com.erudio.web.controllers. A estrutura de pacotes deve ficar similar a imagem abaixo.
Feito isto crie uma classe chamada Person, no pacote br.com.erudio.models, com as informações que serão representadas pelo nosso endpoint REST.
package br.com.erudio.models; import java.io.Serializable; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Person implements Serializable{ private static final long serialVersionUID = 1L; private Long id; private String firstName; private String lastName; private String address; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Feito isto crie uma interface com o nome PersonService no pacote br.com.erudio.models.
package br.com.erudio.services; import java.util.List; import br.com.erudio.models.Person; public interface PersonService { Person create(final Person person); Person findById(final String personId); List<person> findAll(); Person update(Person person); void delete(final String personId); }
Agora vamos criar a implementação para a interface PersonService. No pacote br.com.erudio.services.implementations crie a classe PersonServiceImpl que implementa a interface que acabamos de criar.
package br.com.erudio.services.implementations; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.springframework.stereotype.Service; import br.com.erudio.models.Person; import br.com.erudio.services.PersonService; @Service public class PersonServiceImpl implements PersonService { // Contador responsável por gerar um fake ID já que não estamos // acessando nenhum banco de dados private final AtomicLong counter = new AtomicLong(); // Metodo responsável por criar uma nova pessoa // Se tivéssemos um banco de dados esse seria o // momento de persistir os dados @Override public Person create(Person person) { return person; } // Método responsável por retornar uma pessoa // como não acessamos nenhuma base de dados // estamos retornando um mock @Override public Person findById(String personId) { Person person = new Person(); person.setId(counter.incrementAndGet()); person.setFirstName("Leandro"); person.setLastName("Costa"); person.setAddress("Uberlândia - Minas Gerais - Brasil"); return person; } // Método responsável por retornar todas as pessoas // mais uma vez essas informações são mocks @Override public List<person> findAll() { ArrayList<person> persons = new ArrayList<>(); for (int i = 0; i < 8; i++) { Person person = mockPerson(i); persons.add(person); } return persons; } // Método responsável por atualizar uma pessoa // por ser mock retornamos a mesma informação passada @Override public Person update(Person person) { return person; } // Método responsável por deletar // uma pessoa a partir de um ID @Override public void delete(String personId) { } // Método responsável por mockar uma pessoa private Person mockPerson(int i) { Person person = new Person(); person.setId(counter.incrementAndGet()); person.setFirstName("Person Name " + i); person.setLastName("Last Name " + i); person.setAddress("Some Address in Brasil " + i); return person; } }
Por fim crie a classe PersonController, responsável por expor o endpoint de pessoas, no pacote br.com.erudio.web.controllers.
package br.com.erudio.web.controllers; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import br.com.erudio.models.Person; import br.com.erudio.services.PersonService; @RestController //Mapeia as requisições de localhost:8080/person/ @RequestMapping("/person/") public class PersonController { @Autowired private PersonService personService; @ResponseStatus(HttpStatus.OK) //Por padrão responde com o status code 200 success @RequestMapping(value = "/{personId}", //Mapeia as requisições GET para localhost:8080/person/ //recebendo um ID como @PathVariable method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) // Produz JSON como retorno public Person get(@PathVariable(value = "personId") String personId){ return personService.findById(personId); } @ResponseStatus(HttpStatus.OK) //Por padrão responde com o status code 200 success @RequestMapping(value = "/findAll", //Mapeia as requisições GET para localhost:8080/person/findAll method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) // Produz JSON como retorno public List<person> findAll(){ return personService.findAll(); } @ResponseStatus(HttpStatus.OK) //Por padrão responde com o status code 200 success @RequestMapping(method = RequestMethod.PUT, //Mapeia as requisições PUT para localhost:8080/person/ consumes = MediaType.APPLICATION_JSON_VALUE, // Consome JSON enviado no corpo da requisição produces = MediaType.APPLICATION_JSON_VALUE) // Produz JSON como retorno public Person create(@RequestBody Person person){ return personService.create(person); } @ResponseStatus(HttpStatus.OK) //Por padrão responde com o status code 200 success @RequestMapping(method = RequestMethod.POST, //Mapeia as requisições POST para localhost:8080/person/ consumes = MediaType.APPLICATION_JSON_VALUE) // Consome JSON enviado no corpo da requisição public Person update(@RequestBody Person person){ return personService.update(person); } @ResponseStatus(HttpStatus.OK) //Por padrão responde com o status code 200 success @RequestMapping(value = "/{personId}", method = RequestMethod.DELETE) //Mapeia as requisições DELETE para localhost:8080/person/ //recebendo um ID como @PathVariable public void delete(@PathVariable(value = "personId") String personId){ personService.delete(personId); } }
Agora que terminamos nossa codificação acesse a classe Application e inicie a aplicação como no post anterior. Para testar nosso endpoint e cada uma de suas operações usaremos o plugin do Chrome Postman.
Primeiro faremos uma requisição do tipo GET chamando a operação findAll para o recurso localhost:8080/person/findAll. Como se pode ver na imagem abaixo obtemos como resposta um JSON com um array de pessoas.
Agora faremos uma requisição do tipo GET chamando a operação localhost:8080/person/1 passando como @PathVariable o ID da pessoa que desejamos recuperar. Da mesma forma que na requisição anterior recebemos como resposta um JSON com 1 objeto pessoa.
Agora faremos uma requisição do tipo POST (esse é o verbo REST usado para persistir informações) chamando a operação localhost:8080/person/ passando como parâmetro um JSON com um objeto pessoa no corpo da requisição. Para isso no Postman precisamos setar o tipo de requisição como POST, na aba Body definimos o JSON que representa o objeto pessoa a ser gravado, selecionamos a opção raw, definimos a opção JSON(application/json) e por fim executamos a request através do botão send.
Na imagem abaixo podemos ver o body da response retornada com um ID definido pela aplicação.
Agora faremos uma requisição do tipo PUT (esse é o verbo REST usado para atualizar informações) chamando a operação localhost:8080/person/ passando como parâmetro um JSON com um objeto pessoa no corpo da requisição. Lembrando que essa representação de pessoa deve ter um ID que será usado pra localizar e atualizar um recurso na base. Para isso no Postman precisamos setar informações similares ao post a unica diferença é que o tipo de requisição deve ser o PUT.
Como se pode ver na imagem abaixo o body da response retorna o nosso objeto JSON com as alterações.
Por fim faremos uma requisição do tipo DELETE (verbo REST usado excluir informações) chamando a operação localhost:8080/person/1 passando como @PathVariable o ID da pessoa que desejamos remover da base. Esse tipo de requisição retorna como resposta um body vazio e um StatusCode 200 caso a operação seja executada com sucesso os StatusCode 204 (no content), 401 (Unauthorized), 403 (forbiden), 404 (not found) ou 500 (internal server error) podem ser retornados.
Dessa forma abordamos os 4 verbos principais do HTTP usados em aplicações REST. Lembrando que você pode baixar o código aqui e descompactar o arquivo zip e importar não sua IDE de preferencia ou clonar usando Git:
git clone https://github.com/leandrocgsi/simple-rest-example-verbs.git
Continue ligado no blog, por que no próximo post iremos documentar a nossa API com o framewrok Swagger. E claro a abordagem nesses posts também será totalmente mão na massa. É isso aí bons estudos.
Treinamentos relacionados com este post
Cara show esses seus tutoriais, simples, práticos e eficaz!
Obrigado Bruno.
Olá Leandro, estou iniciando neste universo e estou fazendo testes com o código que tu disponibilizou. Quando eu testo o POST, me retorna o seguinte erro no Postman:
{
“timestamp”: 1572026545344,
“status”: 400,
“error”: “Bad Request”,
“exception”: “org.springframework.http.converter.HttpMessageNotReadableException”,
“message”: “Required request body is missing: public br.com.erudio.models.Person br.com.erudio.web.controllers.PersonController.create(br.com.erudio.models.Person)”,
“path”: “/person/”
}