Posts com Tags API

Aplicações RESTful HATEOAS com SpringBoot


O HATEOAS, é uma das principais constraints arquiteturais do REST e possibilita a navegação entre recursos, que são representações dos modelos de negócio da aplicação. Esse post é bem mão na massa e se quiser aprofundar um pouco mais em conceitos teóricos de HATEOAS confira o nosso post Entendendo HATEOAS. Vamos tomar como base a aplicação desenvolvida no nosso outro post Documentando aplicações REST com SpringBoot e Swagger. Para começar 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


Primeiro altere o pom.xml adicionando os trechos destacados por comentários 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-hateoas</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <!-- Adicione a dependencia de HATEOAS-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        
        <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>
        
        <!-- Adicione as dependencias de teste -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</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>
        <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>


Primeiro vamos alterar a classe Greeting que agora ira extender ResourceSupport.


package br.com.erudio.models;

import org.springframework.hateoas.ResourceSupport;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Greeting extends ResourceSupport {

    private final long idGreeting;
    private final String content;

    @JsonCreator
    public Greeting(@JsonProperty("id") long id, @JsonProperty("content") String content) {
        this.idGreeting = id;
        this.content = content;
    }

    public long getIdGreeting() {
        return idGreeting;
    }

    public String getContent() {
        return content;
    }
}


Agora adicione os trechos comentados a classe GreetingController.


package br.com.erudio.web.controllers;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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")
@RestController
@RequestMapping("/greeting")
public class GreetingController {

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

	@ApiOperation(value = "Show Greeting Message" )
	@ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public HttpEntity<Greeting> greeting(@RequestParam(value="name", defaultValue="World") String name) {
		Greeting greeting = new Greeting(counter.incrementAndGet(), String.format(template, name));
		
		// Na prática essa linha adiciona uma auto referência ao próprio endpoint
		// e apenas esse pequeno trecho de código já é o suficiente para que o endpoint 
		// greeting seja HATEOAS
		greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());
        
		return new ResponseEntity<Greeting>(greeting, HttpStatus.OK);
    }
}


Se você iniciar a aplicação e acessar o endereço localhost:8080/greeting verá algo similar a imagem abaixo.

postagem_4_0


package br.com.erudio.models;

import java.io.Serializable;

import org.springframework.hateoas.ResourceSupport;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

// Adicione a anotação @JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true)

//Extenda ResourceSupport
public class Person extends ResourceSupport implements Serializable{
    
    private static final long serialVersionUID = 1L;
    
	// Por padrão implementação HATEOAS do Spring tem um atributo id
	// como default por isso o ID de nossa entidade deve ser alterado
    private Long idPerson;
    private String firstName;
    private String lastName;
    private String address;
    
    public Person() {}

	public Long getIdPerson() {
        return idPerson;
    }
    
    public void setIdPerson(Long id) {
        this.idPerson = 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;
    }
}


Agora vamos alterar a classe PersonController:


package br.com.erudio.web.controllers;
 
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
 
import java.util.ArrayList;
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")
@RestController
@RequestMapping("/person/")
public class PersonController {
     
    @Autowired
    private PersonService personService;
	@ApiOperation(value = "Find person by ID" )
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{personId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public Person get(@PathVariable(value = "personId") String personId){
        Person person = personService.findById(personId);
		
		//Adicione uma auto referencia ao método get do controller passando o ID como parâmetro
        person.add(linkTo(methodOn(PersonController.class).get(personId)).withSelfRel());
		return person;
    }
     
    .
    .
    .
     
}


Note que ao acessar esse recurso com uma ferramenta como o POSTman teremos uma resultado similar a imagem abaixo.


postagem_4_1


Agora vamos alterar o findAll:


package br.com.erudio.web.controllers;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.ArrayList;
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")
@RestController
@RequestMapping("/person/")
public class PersonController {
	
	@Autowired
	private PersonService personService;
	
	.
	.
	.
	
	@ApiOperation(value = "Find all persons" )
	@ResponseStatus(HttpStatus.OK)
	@RequestMapping(value = "/findAll", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public List<Person> findAll(){
		List<Person> persons = personService.findAll();
		ArrayList<Person> personsReturn = new ArrayList<Person>();
		for (Person person : persons) {
			String idPerson = person.getIdPerson().toString();
			
			// Adicione uma referencia ao método get do controller passando o ID como parâmetro
			// isso é feito para todos os elementos da lista
			person.add(linkTo(methodOn(PersonController.class).get(idPerson)).withSelfRel());
			personsReturn.add(person);
		}
		return personsReturn;
	}
	
	.
	.
	.
	
}


A imagem abaixo nos mostra o resultado dessa mudança. A nossa lista nos tras referencias únicas para cada um dos recursos.


postagem_4_2


Agora vamos adicionar o suporte a HATEOAS ao verbo POST da nossa aplicação.


package br.com.erudio.web.controllers;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.ArrayList;
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")
@RestController
@RequestMapping("/person/")
public class PersonController {
	
	@Autowired
	private PersonService personService;
	
	.
	.
	.
	
	@ApiOperation(value = "Create a new person" )
	@ResponseStatus(HttpStatus.OK)
	@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
	public Person create(@RequestBody Person person){
		Person createdPerson = personService.create(person);
		String idPerson = createdPerson.getIdPerson().toString();
		
		// Após criarmos um novo recurso do tipo Person nós recuperamos seu ID e adicionamos
		// uma referencia ao método get do controller passando o ID como parâmetro		
		createdPerson.add(linkTo(methodOn(PersonController.class).get(idPerson)).withSelfRel());
		return createdPerson;
	}
	
	.
	.
	.
	
}


Como se pode ver na imagem após salvar uma nova pessoa a aplicação retorna um link para que as informações da mesma possam ser acessadas.


postagem_4_3


Agora vamos modificar o verbo PUT.


package br.com.erudio.web.controllers;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.ArrayList;
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")
@RestController
@RequestMapping("/person/")
public class PersonController {
	
	@Autowired
	private PersonService personService;
		
	.
	.
	.
	
	@ApiOperation(value = "Update an existing person")
	@ResponseStatus(HttpStatus.OK)
	@RequestMapping(method = RequestMethod.PUT,consumes = MediaType.APPLICATION_JSON_VALUE)
	public Person update(@RequestBody Person person){
		Person updatedPerson = personService.update(person);
		String idPerson = updatedPerson.getIdPerson().toString();
		
		// Após atualizarmos um recurso nós recuperamos seu ID e adicionamos
		// uma referencia ao método get do controller passando o ID como parâmetro		
		updatedPerson.add(linkTo(methodOn(PersonController.class).get(idPerson)).withSelfRel());		
		return updatedPerson;
	}
	
	.
	.
	.
	
}


Da mesma forma que com o verbo POST após atualizar uma pessoa a aplicação retorna um link para que as informações da mesma possam ser acessadas.


postagem_4_4


Como o verbo DELETE exclui um recurso não há necessidade de adicionar suporte a HATEOAS nele. Sendo assim fechamos a nossa implementação e podemos dizer que a nossa API é finalmente RESTful. Assim como nos posts anteriores 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-hateoas.git


É isso aí bons estudos e continuem ligados no blog para mais novidades 😉


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: , , , ,

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: , , , ,

RESTful Web Services

Dando seqüência a série de postagens sobre Web-services neste post, discutiremos sobre a JAX-RS 2.0 a especificação para serviços baseados em RESTful. No mundo REST, você pode acessar tudo como um recurso e eles possuem um identificador único a URI(Uniform Resource Identifier). Um servidor REST e seus clientes interagem usando o protocolo HTTP e seus quatro verbos mais conhecidos que são:


POST – CREATE/para inserir recurso


O verbo HTTP POST é mais freqüentemente usado para criar novos recursos — inserir um novo item na base. Em uma aplicação REST perfeita quando uma operação é executada com sucesso, retorna-se o idempotente portanto seu uso não é recomendado quando se precisa desse tipo de comportamento em uma requisição. Fazendo duas requisições POST idênticas o resultado mais comum será duas respostas iguais com a mesma informação mas ocasionalmente essa resposta pode mudar.


GET – READ/para selecionar/recuperar um recurso


O verbo HTTP GET é usado para ler ou recuperar uma representação de um recurso. Em um “cenário feliz”, uma requisição GET retorna uma representação em XML ou JSON e um HTTP status code 200 (OK). Em um cenário de erro o retorno mais comum é 404 (NOT FOUND) ou 400 (BAD REQUEST).
De acordo com o design da especificação HTTP, o verbo GET (juntamente com o HEAD) é usado apenas para ler informações e não para alterá-la. Portanto quando usado dessa maneira é considerado seguro. Ou seja, ou seja eles podem ser invocados sem risco de perda ou corrupção de dados — chamando uma vez tem o mesmo efeito de chamá-lo 10 vezes, ou nenhuma. Além disso, o verbo GET (e HEAD) é idempotente, o que significa que se fizermos múltiplas requisições teremos sempre o mesmo resultado.
Você nunca deveria expor operações inseguras via GET — ele nunca deve ser usado para modificar informações no servidor.


PUT – UPDATE/para modificar um recurso


O verbo PUT é comumente usado para atualizar informações, colocando um recurso conhecido no (body) corpo da requisição contendo novas informações que representam o recurso original.
Entretanto, o verbo PUT também pode ser usado para criar um recurso caso o ID do mesmo seja definido pelo cliente ao invés do servidor. Em outras palavras se o verbo PUT é proveniente de uma URI que não contenha um recurso com um ID preexistente. Nesse caso o corpo da requisição contém a representação de um recurso. Para muitos isso pode parecer complicado e confuso. Conseqüentemente, este verbo deve ser usado com moderação para a criação de um novo recurso.
Uma alternativa para isso é utilizar o verbo POST para criar novos recursos com o ID definido pelo cliente setado no corpo da requisição — caso não tenha o ID definido para o recurso deve-se usar o verbo POST(confira acima).
Um update bem sucedido, retorna um status code 200 (ou 204 quando não retorna nenhum conteúdo no body. Se estiver usando o verbo PUT para criar um novo recurso, o retorno deve ser um status code 201 em caso de uma criação bem sucedida. O response body é opcional — lembrando que quando ele é exposto há um consumo maior de banda. Caso o cliente seja o responsável por definir o ID do recurso não é necessário retornar um link para o mesmo via location header.
O verbo PUT não é uma operação segura, na medida em que modifica(ou cria) um recurso no servidor, mas ele é idempotente. Em outras palavras, se você cria ou atualiza um recurso usando o verbo PUT e executa a mesma operação de novo, o recurso terá o mesmo estado que na primeira execução.
Se, por exemplo, você fizer uma requisição PUT para um recurso que incrementa um contador dentro de um recurso, a requisição perde o comportamento de idempotente. As vezes isso acontece sendo suficiente apenas documentar a requisição como não idempotente. Entretanto, o recomendado para requisições PUT é implementar apenas requisições idempotentes. Sendo assim é fortemente recomendado o uso do verbo POST para operações não idempotentes.


DELETE – DELETE/para remover um recurso


O verbo DELETE é fácil de entender, ele é usado para deletar um recurso identificado por uma URI.
Em uma deleção bem sucedida retorna-se um status code 200 (OK) juntamente com um response body, possivelmente uma representação do item deletado (o que acaba por demandar muita banda), ou uma response customizada. Ou retornar o status code 204 (NO CONTENT) sem response body. Em outras palavras, um status code 204 sem corpo, ou JSEND-style response com um status code 200 são as responses mais recomendadas.
As operações de DELETE são idempotentes. Se você deleta um recurso, ele é removido sendo impossível repetir a operação uma vez que o recurso não existe mais. Se você utilizar o verbo DELETE para decrementar um contador (dentro de um recurso), sua operação DELETE não será mais idempotente. Como dito anteriormente, estatísticas de uso e métricas podem ser atualizadas enquanto o serviço é considerado idempotente e as informações do recursos permanecem inalteradas. É recomendado o uso do verbo POST para operações não idempotentes.
Existe uma recomendação quanto a idempotência do DELETE entretanto repetir uma requisição de DELETE em um recurso, normalmente, retorna um status code 404 (NOT FOUND) uma vez que já foi removido e, portanto, não existe mais. Para muitos uma operação de DELETE não é idempotente, entretanto, o resultado final do recurso é o mesmo. Retornar um status code 404 é aceitável e comunica com precisão o status da requisição.


Como podemos perceber, esses quatro verbos nos permite traçar uma certa analogia entre os verbos HTTP e o SQL. Observando a tabela abaixo isto fica um pouco mais claro.


image2


Além dos verbos mencionados anteriormente temos outros menos usados.


HEAD – O verbo HEAD possui uma funcionalidade similar ao verbo GET, exceto pelo fato do servidor retornar uma response line e headers, mas sem um entity-body.
TRACE – O verbo TRACE é usado para recuperar o conteúdo de uma requisição HTTP de volta podendo ser usado com o propósito de debug durante o processo de desenvolvimento.
OPTIONS – O verbo OPTIONS é usado pelo cliente para encontrar operações HTTP e outras opções suportadas pelo servidor. O cliente pode especificar uma URL para o verbo OPTIONS ou um asterisco (*) para se referir a todo o servidor.
CONNECT – O verbo CONNECT é usado pelo cliente para estabelecer uma conexão de rede com um servidor via HTTP.
PATCH – O verbo PATCH pode ser usado para realizar updates parcias de um recurso. Por exemplo, quando você precisar alterar apenas um campo em um recurso, executar um POST com todo o objeto é pesado e acarreta em um maior consumo de banda. Levando isso em conta a primeira coisa que vem em mente é: “por que não usar o PATCH ao invés do PUT para realisar atualizações sempre?”. Simples o verbo PATCH pode ser um problema na medida em que não é idempotente. Além disso minimizar seu uso previne curupção de dados por “colisões” entre duas PATCH requests no mesmo recurso em um mesmo frame de tempo. Colisões entre multiplas PATCH requests são mais perigosas que colisõs entre PUT requests por que exige que o cliente tenha informações básicas do recurso ou irão corrompê-lo.


Formatos Suportados em Web Services RESTful


● XML
● JSON
● CSV
● Texto
● Imagens
● HTML
● PDF
● binário
● etc


Vantagens dos Web Services RESTful


REST é um padrão arquitetural basicamente leve por natureza. Então quando você tiver limitações de banda prefira web services REST;
● Desenvolvimento fácil e rápido;
● Grandes sites como Twitter, Yahoo, Google, Facebook usam este padrão.
● Aplicativos Mobile tem ganhado cada vez mais espaço e precisam interagir rapidamente com os servidores e o padrão REST é mais rápido no processamento de dados das requests e responses.


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


Apache CXF
JBoss RestEasy
Jersey
Redhat
Elkstein
DZone
REST API Tutorial
The RESTful CookBook
Tutorials Point

Tags: , , , ,