API RESTful com spring framework

Bom neste projeto vamos fazer uma API Restful usando o spring framework por intermédio de alguns de seus sub projetos, sera usado para este projeto as seguinte tecnologias

  • spring boot 1.5.10
  • Maven
  • spring data JPA
  • postgresql
  • Java 8
  • Postma para testar no API
  • eclipse ( com plugin STS )

Este Projeto foi criado usando o eclipse
new -> Other -> Spring Starter Project

criando_projeto00

esta imagem mostra como estruturei o projeto dentro do eclipse

post1

LivroRepository.java

fazendo o extends da interface JpaRepository do Spring temos ao nosso dispor varios metodos uteis para operações de CRUD como pode ser visto abaixo

jparepository

pessoal na classe abaixo o extends JpaRepository deve esta assim JpaRepository<Livro, Long> que é a classe de entidade e o tipo da sua chave primaria.

package br.com.restful.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import br.com.restful.model.Livro;

@Repository
public interface LivroRepository extends JpaRepository {

}

nesta classe injetamos a interface LivroRepository e encapsulamos os metodos de interação com o banco de dados  a annotation @Component serve para torna esta classe gerenciada pelo spring outra particularidade e @Transactional(readOnly=true), este (readOnly=true) serve para informar qual metodo é apenas de leitura

LivroService.java

package br.com.restful.service;

import java.io.Serializable;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import br.com.restful.model.Livro;
import br.com.restful.repository.LivroRepository;

@Component
public class LivroService  {

	@Autowired
	private LivroRepository livroRepository;

	@Transactional
	public Livro save(Livro entity) {
		return livroRepository.save(entity);
	}

	@Transactional(readOnly=true)
	public Livro getById(Serializable id) {
		return livroRepository.findOne((Long) id);
	}

	@Transactional(readOnly=true)
	public List getAll() {
		return livroRepository.findAll();
	}

	@Transactional
	public void delete(Serializable id) {
		livroRepository.delete((Long) id);
	}

}

esta é um simples POJO configurada com o JPA

Livro.java

package br.com.restful.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "livro")
public class Livro implements java.io.Serializable {

	private static final long serialVersionUID = 4910225916550731446L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id", unique = true, nullable = false)
	private Long id;

	@Column(name = "isbn")
	private String isbn;

	@Column(name = "titulo")
	private String titulo;

	@Column(name = "descricao")
	private String descricao;

	@Column(name = "preco")
	private Integer preco;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getIsbn() {
		return isbn;
	}

	public void setIsbn(String isbn) {
		this.isbn = isbn;
	}

	public String getTitulo() {
		return titulo;
	}

	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public Integer getPreco() {
		return preco;
	}

	public void setPreco(Integer preco) {
		this.preco = preco;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Livro other = (Livro) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Livro [id=" + id + ", isbn=" + isbn + ", titulo=" + titulo + ", descricao=" + descricao + ", preco="
				+ preco + "]";
	}

}

Esta é um simples POJO onde informações que serão mostradas quando acontecer alguma exception na nossa aplicação

DetalheErro.java

package br.com.restful.exception;

public class DetalheErro {

	private int errorCode;
	private String detalhe;

	public int getErrorCode() {
		return errorCode;
	}

	public void setErrorCode(int errorCode) {
		this.errorCode = errorCode;
	}

	public String getDetalhe() {
		return detalhe;
	}

	public void setDetalhe(String detalhe) {
		this.detalhe = detalhe;
	}

}

nesta classe criamos uma exception personalizada para ser lançada em algumas particularidades da nossa aplicação

RestException.java

package br.com.restful.exception;

public class RestException extends Exception {

	private static final long serialVersionUID = -4852716389437651863L;

	private String errorMessage;

	public String getErrorMessage() {
		return errorMessage;
	}

	public RestException(String errorMessage) {
		super(errorMessage);
		this.errorMessage = errorMessage;
	}

	public RestException() {
		super();
	}

}

Nesta classe em cada metodo anotado com @ExceptionHandler(Exception.class) e possível de acordo com a exceção lancada montar um response tratado com todas as informações que quisermos enviar para quem estiver consumindo nossa api

RestExceptionHandler.java

package br.com.restful.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class RestExceptionHandler {

	@ExceptionHandler(RestException.class)
	public ResponseEntity restException(Exception ex) {
		DetalheErro error = new DetalheErro();
		error.setErrorCode(HttpStatus.BAD_REQUEST.value());
		error.setDetalhe(ex.getMessage());
		return new ResponseEntity(error, HttpStatus.NOT_FOUND);
	}

	@ExceptionHandler(Exception.class)
	public ResponseEntity exceptionHandler(Exception ex) {
		DetalheErro error = new DetalheErro();
		error.setErrorCode(HttpStatus.BAD_REQUEST.value());
		error.setDetalhe("Um exceção foi lançada.");
		return new ResponseEntity(error, HttpStatus.BAD_REQUEST);
	}

}

aqui no controller rest tentei fazer o mas auto explicativo possível e também seguindo algumas boas praticas de programação de api rest como quando adicionamos um novo recurso enviamos um HttpStatus.CREATED ao cliente ou entao quando buscamos um recurso com sucesso no banco retornamos um HttpStatus.OK

LivrariaController.java

package br.com.restful.controller;

import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RestController;

import br.com.restful.exception.RestException;
import br.com.restful.model.Livro;
import br.com.restful.service.LivroService;

@RestController
@RequestMapping("/livros")
public class LivrariaController {

	final static Logger logger = Logger.getLogger(LivrariaController.class);

	@Autowired
	LivroService livroService;

	@RequestMapping(method = RequestMethod.POST)
	public ResponseEntity adicionarLivros(@RequestBody Livro livro) {
		livroService.save(livro);
		logger.debug("Livro adicionado:: " + livro);
		return new ResponseEntity(livro, HttpStatus.CREATED);
	}

	@RequestMapping(method = RequestMethod.PUT)
	public ResponseEntity atualizarLivros(@RequestBody Livro livro) throws RestException {
		Livro existingLiv = livroService.getById(livro.getId());
		if (existingLiv == null) {
			throw new RestException("erro nao atualizacao de livro");
		} else {
			livroService.save(livro);
			return new ResponseEntity(HttpStatus.OK);
		}
	}

	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public ResponseEntity buscarLivro(@PathVariable("id") Long id) throws RestException {
		Livro livro = livroService.getById(id);
		if (livro == null) {
			throw new RestException("Livro com id "+id+" nao existe");
		}
		logger.debug("Livro :: " + livro);
		return new ResponseEntity(livro, HttpStatus.OK);
	}

	@RequestMapping(method = RequestMethod.GET)
	public ResponseEntity buscarTodosLivros() {
		List livro = livroService.getAll();
		if (livro.isEmpty()) {
			logger.debug("Nao existem livros cadastrados");
			return new ResponseEntity(HttpStatus.NO_CONTENT);
		}

		logger.debug(Arrays.toString(livro.toArray()));
		return new ResponseEntity(livro, HttpStatus.OK);
	}

	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	public ResponseEntity apagarLivros(@PathVariable("id") Long id) throws RestException {
		Livro livro = livroService.getById(id);
		if (livro == null) {

			throw new RestException("Livro com id "+id+" nao existe");

		} else {
			livroService.delete(id);
			logger.debug("Livro com id " + id + " deletado");
			return new ResponseEntity(HttpStatus.GONE);
		}
	}

}

Esta é a classe responsavel por subir nossa aplicação com spring boot

Application.java

package br.com.restful;

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);
	}
}

Este arquivo de propriedades é usado pra configurar a aplicação em varios sentidos como a configuração do path da aplicação do banco de dados da jpa e do log

application.properties

# Configuracao do Servidor
server.contextPath = /rest

# Configuracao do banco de dados
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgresql

# JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# Configuracao do log da aplicacao
logging.level.com.bytestree.restful=DEBUG

Agora vamos testar a nossa aplicação

Adicionando:

postman-adicionando

Listando:

postman-listando

Atualizando:

postman-atualizado

Deletando:

postman-deletando

Listando por Id:

postman-listandoPorId

Bom pessoal por hora é só. a ideia é evoluir este projeto em alguns post a mais incluindo talvez teste automatizado, integracao com swagger ou quem sabe criar um cliente para esta API em VueJs

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

w

Conectando a %s

Crie um website ou blog gratuito no WordPress.com.

Acima ↑

%d blogueiros gostam disto: