Posts com Tags REST

Testando serviços REST com Spring Boot e RestTemplate

Uma das principais características do Spring Boot é a sua simplicidade em permitir criar rapidamente uma aplicação sem abrir mão do design do seu código. E se você realmente quer fazer um trabalho de qualidade não pode abrir mão dos testes. Um bom modo de se fazer isso é com testes efetivos (não mocks) rodando no seu ambiente desenvolvimento. No trecho de códigos criamos o nosso primeiro teste.

package br.com.erudio;

import java.io.IOException;
import java.util.List;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import br.com.erudio.models.Person;
import br.com.erudio.repository.PersonRepository;

//Inicializamos o SpringRunner
@RunWith(SpringRunner.class)
//Setamos como uma classe de testes com o Server inicializando em uma porta fixa
//Essa porta pode ser a 8080 ou a que desejarmos setada no nosso aplication.properties
//Acesse src/test/resources/application.properties caso deseje alterar
//No nosso caso é a 8888 como você pode ver no aplication.properties "server.port = 8888"
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class PersonEndpointTests {
    
    //URL base para onde as requests serão feitas
    final String BASE_PATH = "http://localhost:8888/person";

    //Injetamos o repositório para acesso ao Banco de dados
    @Autowired
    private PersonRepository repository;
	
    //Definimos o restTemplate
    private RestTemplate restTemplate;
    
    //Definimos o JacksonMapper responsável por converter
    //JSON para Objeto e vice versa
    private ObjectMapper MAPPER = new ObjectMapper();
    
    //Definimos o que será feito antes da execução do teste
    @Before
    public void setUp() throws Exception {

        //Deletamos todos os registros do banco
        repository.deleteAll();

        //Inserimos alguma pessoas no banco
        repository.save(new Person("Diego", "Samuel", "Asa Sul"));
        repository.save(new Person("Eudes", "Silva", "Rosário"));
        repository.save(new Person("Anderson", "Silva", "Santa Teresa"));
	repository.save(new Person("Alice", "Ferreira", "Bombeiros"));
	repository.save(new Person("Alan", "Franco", "Granja Marileusa"));        
       
        //Inicializamos o objeto restTemplate
        restTemplate = new RestTemplate();
    }
    
    @Test
    public void testCreatePerson() throws JsonProcessingException{

        //Criamos uma nova pessoa
        Person person = new Person("Leandro", "Costa", "Presidente Roosevelt");

        //Fazemos um HTTP request do tipo POST passando a pessoa como parâmetro
        Person response = restTemplate.postForObject(BASE_PATH, person, Person.class);

        //Verificamos se o resultado da requisição é igual ao esperado
        //Se sim significa que tudo correu bem
        Assert.assertEquals("Leandro Costa", response.getFirstName() + " " + response.getLastName());
    }
    
  

}

Agora faremos um HTTP request do tipo GET para listar todas as pessoas ou uma em específico

    @Test
    public void testFindOne() throws IOException{

        //Fazemos uma requisição HTTP GET buscando por todas as pessoas
    	String response = restTemplate
			.getForObject(BASE_PATH + "/findAll", String.class);

        //Convertemos a resposta JSON para Objeto usando op Jackson
        List<Person> persons = MAPPER.readValue(response, 
			MAPPER.getTypeFactory().constructCollectionType(List.class, Person.class));
        //Pegamos o ID da pessoa na posição 1 da lista e fazemos nova requisição
        //Recuperando as informações da mesma
        Person person = restTemplate.getForObject(BASE_PATH + "/" +
			persons.get(1).getIdPerson(), Person.class);
        
        //Verificamos se o resultado da requisição é igual ao esperado
        //Se sim significa que tudo correu bem
        Assert.assertNotNull(person);
    	Assert.assertEquals("Eudes", person.getFirstName());
    	Assert.assertEquals("Silva", person.getLastName());
    }

Agora faremos um HTTP request do tipo PUT para atualizar uma pessoa em específico

    
    @Test
    public void testUpdatePerson() throws IOException{
    	String response = restTemplate.getForObject(BASE_PATH + "/findAll", String.class);
        List<Person> persons = MAPPER.readValue(response,
			MAPPER.getTypeFactory().constructCollectionType(List.class, Person.class));
        
        //Pegamos o ID da pessoa na posição 2 da lista e fazemos nova requisição
        //Recuperando as informações da mesma
        Person person = restTemplate.getForObject(BASE_PATH + "/" +
			persons.get(2).getIdPerson(), Person.class);

        //Alteramos as informações da pessoa recuperada
        person.setFirstName("Tiago");
        person.setLastName("Ferreira");

        //Fazemos um HTTP request do tipo PUT passando a pessoa 
        //e suas novas informações como parâmetro
        restTemplate.put(BASE_PATH, person);
        
        //como a operação PUT do RestTemplate é do tipo void
        //Precisamos fazer uma nova requisição para recuperar 
        //as informações da pessoa atualizada
        Person person2 = restTemplate.getForObject(BASE_PATH + "/" +
			persons.get(2).getIdPerson(), Person.class);
       
        //Verificamos se o resultado da requisição é igual ao esperado
        //Se sim significa que tudo correu bem
        Assert.assertNotNull(person2);
    	Assert.assertEquals("Tiago", person2.getFirstName());
    	Assert.assertEquals("Ferreira", person2.getLastName());
        
    }

Sendo assim nos testamos as operações do nosso endpoint. Se quiser ver mais detalhes você pode baixar o código deste post aqui, descompactar o arquivo zip e importar na sua IDE preferida ou clonar usando Git:


git clone https://github.com/leandrocgsi/spring-boot-rest-with-mongodb-example.git


Você pode acessar outros posts sobre SpringBoot e REST aqui mesmo no blog. É isso aí bons estudos e se gostou da postagem, vá até o topo desta página e torne-se um membro VIP para receber conteúdos exclusivos.

Tags: , ,

Entendendo HATEOAS


HATEOAS (Hypermedia as the Engine of Application State) é uma constraint arquitetural de aplicações REST. Uma API HATEOAS provê informações que permite navegar entre seus endpoints de forma dinâmica visto que inclui links junto às respostas. Esta capacidade a difere de sistemas baseados em SOA e interfaces dirigidas por WSDL(pronuncia-se uísdou). Com SOA, servidores e clientes usualmente devem acessar uma especificação fixa que pode ser acessada em outro lugar na API, ou em um website, ou as vezes distribuído por email.


Nota: Pronunciar HATEOAS não é fácil e pode variar bastante. Algumas pessoas pronunciam algo como “riteos“, outros como “reitos” ou como “reidôs“. Alguns podem se referir a esse conceito arquitetural por hypermedia-driven system – algo como sistema dirigido por hipermídia.


Exemplos


O código a seguir representa um objeto Cliente.


class Cliente {
    String nome;
}


Uma representação JSON tradicional seria algo como:


{ 
    "nome" : "Leandro"
}


Os dados do Cliente estam lá, mas os dados não contém nada de relevante sobre suas conexões. Uma representação baseada em HATEOAS deve ser similar ao JSON abaixo:


{
    "nome": "Leandro",
    "links": [ {
        "rel": "self",
        "href": "http://localhost:8080/Cliente/1"
    } ]
}


A resposta não contém somente o nome de uma pessoa, mas inclui uma URL com o endereço de onde as informações dessa pessoa podem ser localizadas.
rel significa relacionamento. No nosso caso o link autoreferencia a pessoa. Sistemas mais complexos incluem outros tipos de relacionamentos. Por exemplo, uma ordem de compra pode ter um relacionamento com cliente”rel”:”Cliente” vinculando a ordem ao Cliente.
href é uma URL completa que define um único recurso.


Nota: Apesar de meus exemplos serem em JSON, XML também é aceito como um formato de resposta padrão. HATEOAS não impõe a exigência à um formato em específico. O seu foco é prover links que possibilite navegar entre os recursos de uma API.


Apesar de nossos exemplos serem bem simples é possível construir relações mais complexas. O HATEOAS, facilita aos desenvolvedores, de aplicações cliente, acessar os recursos de uma API sem precisar definir uma especificação, criar um documento externo ou uma wiki para isso.
Observe o JSON abaixo:


{
    "conteudo": [ {
        "preco": 499.00,
        "descricao": "HD Seagate 2TB",
        "nome": "HD S2TB",
        "links": [ {
            "rel": "self",
            "href": "http://localhost:8080/produto/1"
        } ],
        "atributos": {
            "conector": "SATA"
        }
    }, {
        "preco": 49.00,
        "descricao": "Mouse Óptico Dell",
        "nome": " Mouse",
        "links": [ {
            "rel": "self",
            "href": "http://localhost:8080/produto/3"
        } ],
        "atributos": {
            "conector": "wireless"
        }
    } ],
    "links": [ {
        "rel": "produto.consulta",
        "href": "http://localhost:8080/produto/consulta"
    } ]
}

Não apenas os itens e seus precos mostrados, mas também uma URL para cada recurso, fornecendo informações claras sobre como acessar cada recurso. De acordo com o modelo de maturidade de Richardson, HATEOAS é considerado o ultimo nível do REST. Isto significa que em uma aplicação HATEOAS presume-se que os verbos padrão de uma aplicação REST como GET, POST, PUT e DELETE também são adotados. Sendo que cada um deles, como é mostrado acima, provê ao cliente os links necessários ao acesso à uma informação.


É isso aí continuem ligados no blog e em breve teremos uma postagem bem mão na massa com HATEOAS e o nosso velho conhecido Spring Boot. Bons estudos 😉

Tags: , , ,

Documentando aplicações REST com SpringBoot e Swagger



O Swagger é um dos frameworks mais usados para se documentar API’s REST. Ele facilita para que os clientes que consomem nossas API’s saibam quais os parâmetros nossas operações recebem, qual o retorno, o modelo, o media type retornado JSON, XML, CSV, binário etc. Sendo assim os clientes não precisam necessariamente discutir com a equipe de desenvolvimento da API sobre como usá-la. Pode-se dizer que a grosso modo o Swagger se comporta como o velho WSDL das aplicações SOAP. Sendo assim esse framework valoriza e muito nossas aplicações.


Tomando como base o projeto do post anterior vamos adicionar as configurações necessárias para habilitar o Swagger na aplicação. Para isso abra o arquivo pom.xml e adicione as tags destacadas com comentários no código abaixo.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>br.com.erudio</groupId>
	<artifactId>simple-rest-example-swagger</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>
    
    <dependencies>
    	
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
    	<!-- Adicionando a dependencia do Spring Boot Starter Actuator -->
    	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		
		<!-- Adicione as dependências do Swagger API necessárias para gerar a documentação da aplicação-->
		<dependency>
			<groupId>com.mangofactory</groupId>
			<artifactId>swagger-springmvc</artifactId>
			<version>1.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.ajar</groupId>
			<artifactId>swagger-spring-mvc-ui</artifactId>
			<version>0.4</version>
		</dependency>
		<dependency>
    		<groupId>org.apache.tomcat.embed</groupId>
    		<artifactId>tomcat-embed-jasper</artifactId>
    		<scope>provided</scope>
		</dependency>
		
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
   		
   		<!-- Adicione o repositório do Swagger ao projeto-->
		<repository>
			<id>jcenter-release</id>
			<name>jcenter</name>
			<url>http://oss.jfrog.org/artifactory/oss-release-local/</url>
		</repository>
    </repositories>
    
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
	
</project>



Feito isto precisamos alterar a classe Application para que ela acione o Swagger quando inicializar a aplicação.


package br.com.erudio;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.mangofactory.swagger.configuration.SpringSwaggerConfig;
import com.mangofactory.swagger.models.dto.ApiInfo;
import com.mangofactory.swagger.plugin.EnableSwagger;
import com.mangofactory.swagger.plugin.SwaggerSpringMvcPlugin;

@Configuration // Define a classe como classe de configuração
@EnableAutoConfiguration // Habilita a autoconfiguração
@EnableSwagger //Habilita o Swagger
@ComponentScan(basePackages = {"br.com.erudio"}) //Escaneia todos os pacotes com o padrão br.com.erudio
public class Application {
    
	//Injeta uma instancia de SpringSwaggerConfig
    @Autowired
    private SpringSwaggerConfig swaggerConfig;
    
    public static void main(String[] args) {
		   //Troque esta linha SpringApplication.run(Application.class, args); pela linha abaixo
           new SpringApplicationBuilder(Application.class).web(true).run(args);
    }
    
    @Bean
    public SwaggerSpringMvcPlugin groupOnePlugin() {
       return new SwaggerSpringMvcPlugin(swaggerConfig)
			//Adiciona as configurações do Swagger ao SwaggerSpringMvcPlugin 
           .apiInfo(apiInfo()) //Adiciona as propriedades de configuração
           .includePatterns("/person.*?", "/greeting.*?") //Habilita o Swagger para os nossos 2 endpoints
           .swaggerGroup("admin");
    }
    
    private ApiInfo apiInfo() {
       ApiInfo apiInfo = new ApiInfo( //Configurações de contato, licença etc não nescessáriamente precisa ser definida
             "Swagger With Spring Boot",
             "This is a simple application to demonstrate how to work with Swagger in Spring Boot project!",
             "Free to use and mess around",
             "erudio@gmail.com",
             "Open Licence",
             "myemail@gmail.com"
       );
       return apiInfo;
    }
}



Agora que o Swagger já está configurado vamos documentar o endpoint Greeting.


package br.com.erudio.web.controllers;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;

import br.com.erudio.models.Greeting;

@Api(value = "greeting") //Diz ao Swagger que esse é um endpoint e REST deve ser documentado
@RestController
@RequestMapping("/greeting")
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

	@ApiOperation(value = "Show Greeting Message" )
	 //Diz ao Swagger que essa operação REST deve ser documentado
	@ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}



Como se pode ver é extremamente simples documentar a nossa API agora vamos vazer o mesmo com o endpoint Person.


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 com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;

import br.com.erudio.models.Person;
import br.com.erudio.services.PersonService;


@Api(value = "person") //Diz ao Swagger que esse é um endpoint e REST deve ser documentado
@RestController
@RequestMapping("/person/")
public class PersonController {
	
	@Autowired
	private PersonService personService;
	
	@ApiOperation(value = "Find person by ID" )
	//Diz ao Swagger que essa operação REST deve ser documentado
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{personId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public Person get(@PathVariable(value = "personId") String personId){
        return personService.findById(personId);
    }
	
	@ApiOperation(value = "Find all persons" )
	//Diz ao Swagger que essa operação REST deve ser documentado
	@ResponseStatus(HttpStatus.OK)
	@RequestMapping(value = "/findAll", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public List<Person> findAll(){
		return personService.findAll();
	}
	
	@ApiOperation(value = "Create a new person" )
	//Diz ao Swagger que essa operação REST deve ser documentado
	@ResponseStatus(HttpStatus.OK)
	@RequestMapping(method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
	public Person create(@RequestBody Person person){
		return personService.create(person);
	}
	
	@ApiOperation(value = "Update an existing person")
	//Diz ao Swagger que essa operação REST deve ser documentado
	@ResponseStatus(HttpStatus.OK)
	@RequestMapping(method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_VALUE)
	public Person update(@RequestBody Person person){
		return personService.update(person);
	}

	@ApiOperation(value = "Delete person by ID" )
	//Diz ao Swagger que essa operação REST deve ser documentado
	@ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{personId}", method = RequestMethod.DELETE)
    public void delete(@PathVariable(value = "personId") String personId){
        personService.delete(personId);
    }	
}



Não parece mas já terminamos toda a codificação necessária por adicionar o Swagger à nossa API mais uma vez acesse a classe Application e inicie a aplicação como no post anterior. Após inicializada a aplicação acesse a URL:


	localhost:8080/sdoc.jsp



Essa é a URL padrão da documentação e nunca tentei alterá-la. Ao acessá-la você verá algo similar a imagem abaixo documentando cada uma das operações dos nossos endpoints. Bastando clicar sobre cada uma para expandir e ver todos os detalhes da mesma.



postagem_3_1



Na imagem abaixo podemos ver a documentação da operação findAll. Temos a definição do modelo que ela nos retorna, o tipo de dados que ela retorna no caso JSON, os StatusCode que ela pode retornar, se clicarmos em Try Out o Swagger nos permite testar nossas operações. Ao executarmos esse teste a nossa API nos retornou um array de pessoas, o cabeçalho e o StatusCode 200 OK.



postagem_3_5


Chegamos com este post ao final da série de posts, práticos, sobre serviços REST. Você pode baixar o código deste post aqui e descompactar o arquivo zip e importar na sua IDE preferida ou clonar usando Git:

git clone https://github.com/leandrocgsi/simple-rest-example-swagger.git



Agora você já consegue dar os primeiros passos na criação de serviços REST, conhece conceitos, teóricos e práticos, básicos para executar esse tipo de tarefa. Sendo assim continue ligado no blog, por que no próximos posts iremos detonar o Rock’n Roll com outras tecnologias. É isso aí bons estudos.

Tags: , , , , ,

Criando o primeiro endpoint REST com SpringBoot



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.

Tags: , , , ,

Do zero ao REST em 5 minutos com SpringBoot



Este post visa explicar de forma simples como criar um “hello world” RESTFul web service com Spring Boot.



Fique ligado no blog, por que, como já foi dito, nos próximos 2 posts entraremos em maiores detalhes sobre a sopa de letrinhas dos verbos REST, POST, PUT, GET e DELETE na prática e depois documentaremos a nossa API com o framewrok Swagger. E abordagem nesses posts será totalmente mão na massa. É isso aí bons estudos.

Cerca de 5 minutos
Qualquer IDE de preferência o Eclipse
JDK 1.8 ou superior
Maven 3.33+



Onde baixar os códigos do projeto

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.git



Para mais detalhes e para evoluir este projeto acesse a página de guias de inicialização da fundação Spring. Além disso serão feitos mais 2 posts fechando uma série sobre Spring Boot e RESTful abordando os 4 principais verbos REST e como documentar API’s com o framework Swagger. O código dos próximos posts já está implementado e você pode acessar o de verbos aqui e o com Swagger aqui.



E a teoria como fica?


Se você está lendo este post eu acredito que você conheça o mínimo de teoria sobre REST e Web-Services mas se não conhece temos 3 posts que abordam esses temas de forma teórica sendo um sobre Web Services, outro sobre RESTful Web Services e outro sobre os HTTP Status Codes em Serviços REST mais comuns.

Hora de codar



Então chega de conversa e vamos botar a mão na massa ou melhor no código. Com o Eclipse aberto clique em novo escolha a opção Maven Project como na imagem abaixo.

1



Na próxima janela selecione a opção “Create a simple project(skyp archetype selection)”. Isso “diz” ao Maven que você irá construir seu projeto do zero com uma estrutura padrão Maven mas sem usar nenhum Archetype pronto.

2



Na próxima tela defina o Goup Id do seu projeto, no nosso caso “br.com.erudio”. Defina também o Artifact Id (nome) do seu projeto e a versão. Feito isto clique em next.

3



Se tudo tiver corrido bem você terá um projeto com uma estrutura similar à imagem abaixo. Abra o arquivo “pom.xml” e vamos fazer algumas alterações nele.

4



A estrutura original do pom deve ser algo similar a esta:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>br.com.erudio</groupId>
    <artifactId>simple-rest-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    
</project>



Primeiro informamos o “parentesco” do nosso projeto em relação ao Spring boot. Logo abaixo adicionamos a dependência do Spring Boot Starter Web que como o próprio nome diz é o responsável por inicializar nossa aplicação. Definimos também a versão do Java no nosso caso 1.8. Depois precisamos configurar o processo de build que é extremamente simples. E por fim adicionamos os repositórios de onde o Maven irá baixar as dependências e plug-ins necessários ao projeto. Feito isso nos já temos a estrutura básica de um projeto Spring Boot.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>br.com.erudio</groupId>
    <artifactId>simple-rest-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    
    <!-- Adicionando o Spring Boot ao projeto -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>
    
    <!-- Adicionando a dependencia do Spring Boot Starter Web -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- Configurando a versão do Java -->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    
    <!-- Configurando o processo de build da aplicação -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
    <!-- Adicionando o repositório do Spring de onde o Maven irá baixar as dependências -->
    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    
    <!-- Adicionando o repositório do Spring de onde o Maven irá baixar os plugins -->
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
    
</project>



Como se pode ver o nosso projeto está com um marker de erro e a versão do Java está como 1.5 e não 1.8 como definimos no pom.xml.

7



Clique com o direito sobre o projeto escola a opção Build Path >> Configure Build Path

8



Na aba libraries selecione sua JRE System Library e clique em editar.

9



Na proxima janela selecione o radio Alternate JRE e escolha sua versão do Java e clique em Finish.

10



Como se pode ver agora a versão esta correta, então clique em OK.

11



Além disso temos um Mavem problem etratanto isso é apenas um bug do Eclipse e apesar do warning o projeto executa normalmente. Para remover basta escolher a opção quick fix.

12



Na janela a seguir escolha a opção Update project configuration clique em Finish e adeus warning.

13



Agora que eliminamos todos os warnings o ambiente está pronto. Selecione o source folder src/main/java.

14



Clique com o direito New >> Package

15



Na próxima janela defina o nome do pacote no nosso caso “br.com.erudio”.

16



Feito isso abra o souce folder e clicando sobre o pacote com o botão direito New >> Class

17



Na próxima janela defina o nome Greeting(Saudação em inglês já que é um Hello World) e insira o código abaixo. Como você pode perceber é um simples POJO que representa o objeto que será retornado aos clientes de nossa API.


package br.com.erudio;

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}



Agora crie uma nova classe com o nome GreetingController que é o nosso endpoit que receberá as requisições REST. Como se pode ver as requisições para a url {{contexto}}/greeting invocarão o método greeting que recebe um @RequestParam com um nome. Quando esse valor não é informado o valor padrão é “World”. As requisições para esse endpoint retornam uma saudação com um id automático simulando um acesso a um banco de dados e uma string com a saudação.


package br.com.erudio;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}



Agora vamos definir a classe de inicialização de nossa aplicação chamada Application. Acredite ou não com apenas o trecho de código abaixo você tem sua aplicação pronta sem necessidade de container ou server. Sim o Spring Boot te liberta de Tomcat, Jetty ou os “obesos” JBoss e GlassFish e seus EJB’s.


package br.com.erudio;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}



Com a aplicação pronta clique com o direito dobre a classe Application Run As >> Java Application. Caso você esteja usando a Spring Source Tool Suite (ferramenta baseada no Eclipse com foco em desenvolvimento Spring) como é o meu caso pode excolher a opção Spring Boot App.

18



Se você tiver feito tudo corretamente terá um console parecido com a imagem abaixo observe que o endpoint /greeting foi mapeado e que nossa aplicação iniciou em apenas 4 segundos.

19



Vá no browser de sua preferencia e acesse localhost:8080/greeting e terá uma resposta parecida com a imagem abaixo.

20



Agora adicione um parametro a mais a nossa url localhost:8080/greeting?name=Olá Semeru Blog ou outro parametro de sua preferência e terá uma resposta similar à imagem abaixo.

21



Fique ligado no blog, por que, como já foi dito, nos próximos 2 posts entraremos em maiores detalhes sobre a sopa de letrinhas dos verbos REST, POST, PUT, GET e DELETE na prática e depois documentaremos a nossa API com o framewrok Swagger. E abordagem nesses posts será totalmente mão na massa. É isso aí bons estudos.

Tags: , ,

HTTP Status Codes em Serviços REST



Uma boa prática ao se desenvolver serviços REST é retornar status codes adequados a cada operação executada. Isso tem uma importância ainda maior quando se trata de uma exceção, uma vez que diferentementemente de um serviço SOAP uma aplicação verdadeiramente REST não retorna a exception para o usuário. Sendo assim é fortemente recomendado que ao se desenvolver uma API REST se vá além dos status code 200 e 500 OK e falha respectivamente.


200 OK – Request de criação ou deleção executada com sucesso.


201 Created – Criação de uma fila, topico, fila temporária, tópico temporária, session, producer, consumer, listener, queue browser ou menssagem realizada com sucesso.


204 No Content – deleção de uma fila, tópico, sessão, producer ou listener bem sucedida mas sem retorno de conteúdo.


400 Bad Request – O path informado está em umformato incorreto, um parametro ou valor do corpo da requisição não está formatado corretamente ou um parâmetro onbrigatório não foi informado,
ou está formatado corretamente mas pode estar eventualmente inválido (por exemplo, o ID informado não exite – NullPointerException, o conteúdo retornado é muito grande ou o ID informado já está em uso).


403 Forbidden – O cliente não tem permissão para executar requisições na operação em questão.


404 Not Found – o objeto requisitado pelo path não existe(NullPointerException).


405 Method Not Allowed – O usuário não tem permissão de acesso ao path.


409 Conflict – Um objeto já foi criado com as mesmas informações.


500 Internal Server Error – Ocorreu uma falha no servidor, podendo ser desde uma falha no SQL por exemplo.

A prática de retornar os status code corretos torna seus serviços mais amigáveis ao cliente facilitando a integração com outros sistemas. Nos próximos posts botaremos a mão na massa desenvolvendo a nossa propria API/Serviço REST.


Botando em prática


Em breve estarão disponíveis dois posts Criando o primeiro endpoint REST com SpringBoot e Documentando aplicações RESTfull com SpringBoot e Swagger com exemplos práticos.


Referências


DZone

Java World

StackOverflow

Oracle

Tags: , , , ,