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.

postagem_2_0



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.

postagem_2_2



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.

postagem_2_1

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.

postagem_2_3

Na imagem abaixo podemos ver o body da response retornada com um ID definido pela aplicação.


postagem_2_4

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.



postagem_2_5

Como se pode ver na imagem abaixo o body da response retorna o nosso objeto JSON com as alterações.

postagem_2_6



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.


postagem_2_7


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.