Skip to content

Latest commit

 

History

History
207 lines (115 loc) · 13 KB

README.md

File metadata and controls

207 lines (115 loc) · 13 KB

Treinamento Web Services RESTful e Spring

Cada aula é representada por um commit que está identificado com o nome/número da aula.

Referência para HTTP (Métodos e Status de respostas) - http://tools.ietf.org/html/rfc7231

Aula 2.1 - Criando o projeto

Aula 2.2 - Modelando nosso primeiro recurso

  • Criamos uma classe "LivrosResources" e anotamos com @RestController .

  • Criamos um método chamado "listar" que retorna uma String (“Livro 1, Livro 2”).

  • Mapeamos esse método com @RequestMapping(value=”/livros”, method=RequestMethod.GET).

Aula 2.3 - Criando uma representação para o recurso Livro

  • Criamos uma classe chamada "Livro" e uma classe chamada "Comentario" que é um dos atributos de Livro.

  • Alteramos o método "listar" da classe LivrosResources fazendo com que o seu retorno agora seja "List" e alteramos a implementação para retornar uma lista com dois livros com o nome preenchido.

Aula 2.4 - Utilizando a anotação @JsonInclude

  • Alteramos a classe Livro inserido acima de cada atributo a anotação @JsonInclude(Include.NON_NULL) para suprimir o campo do JSON retornado caso o valor seja nulo.

Aula 2.5 - Interagindo com banco de dados

  • Altermos o pom.xml adicionando as seguintes dependências:

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>
      
      <dependency>
          <groupId>com.h2database</groupId>
          <artifactId>h2</artifactId>
          <scope>runtime</scope>
      </dependency>
      
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
          <version>1.3.5.RELEASE</version>
      </dependency>
    
  • Ao adicionar a dependencia do H2 e subir a aplicação agora é possível acessar o console do H2 na seguinte URI:

    http://localhost:8080/h2-console

  • No console que será mostrado, altere o campo "JDBC URL" para "jdbc:h2:mem:testdb" onde "testdb" é o banco de dados definido pelo Spring Boot para ser o banco de dados de teste.

  • Na classe Livro anotamos com @Entity, o atributo id com @Id e @GeneratedValue(strategy = GenerationType.IDENTITY) e o atributo comentarios com @Transient (por enquanto ficará assim).

  • Criamos uma interface "LivrosRepository" que estende JpaRepository e injetamos essa interface na classe "LivrosResources" alterando a implementação do método "listar" para utilizar esse repository.

Aula 2.6 - Salvando o recurso Livro a partir de um POST

  • Criamos o método "salvar" na classe "LivrosRepository" e mapeamos o recurso com sendo "/livros" com Http Method POST.
  • Mudamos a anotação @RequestMapping para a classe retirando dos médotos o mapeamento "/livros".

Aula 2.7 - Buscando um livro com o uso da anotação @PathVariable

  • Criamos o método "buscar" na classe "LivrosRepository" e mapeamos o recurso com sendo "/livros/{id}" com o Http Method GET.

Aula 2.8 - Deletando o recurso Livro com o DELETE

  • Criamos o método "remover" na classe "LivrosRepository" e mapeamos o recurso com sendo "/livros/{id}" om o Http Method DELETE.

Aula 2.9 - PUT para atualizar o recurso Livro

  • Criamos o método "atualizar" na classe "LivrosRepository" e mapeamos o recurso com sendo "/livros/{id}" om o Http Method PUT.

Aula 2.10 - Tratamento correto das respostas HTTP 404 (Not found) e 201 (Created)

  • Alteramos o método "buscar" na classe "LivrosRepository" trocando o retorno para "ResponseEntity<?>" o que nos permite fazer os tratamentos corretos dos Http Status de retorno do serviço. Caso o recurso não seja encontrado a resposta HTTP será 404 (Not found) e caso seja encontrado será 200 juntamente com os dados do recurso.

  • Alteramos o método "salvar" na classe "LivrosRepository" trocando o retorno para "ResponseEntity< Void >" o que nos permite fazer os tratamentos corretos dos Http Status de retorno do serviço. Caso o recurso seja salvo com sucesso a resposta HTTP será 201 (Created) e no header da resposta será inserido o "Location" com a URI onde o recurso criado poderá ser encontrado.

Aula2.11 - Finalizando o tratamento das respostas

  • Alteramos o método "listar" na classe "LivrosRepository" trocando o retorno para "ResponseEntity<List>" e sua implementação para utilizar esse novo retorno: return ResponseEntity.status(HttpStatus.OK).body(livrosRepository.findAll());

  • Alteramos o método "remover" na classe "LivrosRepository" trocando o retorno para "ResponseEntity< Void >" e tratamos, caso o recurso não possa ser removido por não existir a resposta HTTP será 404 (Not found) e caso consiga remover a resposta HTTP será 204 (No content).

  • Alteramos o método "atualizar" na classe "LivrosRepository" trocando o retorno para "ResponseEntity< Void >" e em conseguindo alterar o recuros a resposta HTTP será 204 (No content).

Aula2.12 - Melhorando o design do nosso código

  • Criamos a classe "LivrosService" que será a nossa camada de negócio e refatoramos toda a classe "LivrosService" para utilizar essa camada e não diretamente o repositório.

  • Criamos uma exceção "LivroNaoEncontradoException" para indicar quando um recurso Livro não for encontrado.

Aula 2.13 - Manipulando erros com @ExceptionHandler e @ControllerAdvice

  • Criamos a classe "ResourceExceptionHandler" para manipular toda exceção que ocorrer nos Resources (controllers).

  • Criamos a classe "DetalhesErro" para criar um body para os erros manipulados pela "ResourceExceptionHandler".

  • Refatoramos a classe "LivrosService" removendo os tratamentos de exceção (try/catch) para as exceções do tipo "LivroNaoEncontradoException".

Aula 2.14 - Adicionando comentários ao recurso Livro

  • Alteramos a classe "Livro" inserindo o mapeamento @OneToMany(mappedBy = "livro") para o atributo "comentarios".

  • Alteramos a classe "Comentario" inserindo as anotações @Entity, @Id, @GeneratedValue(strategy = GenerationType.IDENTITY) e criando um novo atributo Livro e anotando esse novo atributo com @ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = "LIVRO_ID") e @JsonIgnore.

  • Criamos uma nova interface ComentariosRepository que estende JpaRespository<Comentario, Long> para atender a nova entidade e injetamos esse repositório na classe "LivrosService".

  • Criamos o método "salvarComentario" na classe "LivrosService" para efetivamente salvar um comentario em um determinado livro.

  • Criamos o método "adicionarComentario" na classe "LivrosResources" com o seguinte mapeamento: @RequestMapping(value = "/{id}/comentarios", method = RequestMethod.POST)

Aula 2.15 - Listando os comentários do recurso Livro

  • Criamos o método "listarComentarios" na classe "LivrosResources" com o seguinte mapeamento: @RequestMapping(value = "/{id}/comentarios", method = RequestMethod.GET).

  • Criamos o método "listarComentarios" na classe "LivrosService" que busca o livro informado e retorna a lista de comentários existentes nesse livro.

Aula 2.16 - Adicionando o recurso Autor à nossa AP

  • Criamos a classe de dominio "Autor" com os seguintes atributos: "id, nome, dataNascimento, nacionalidade e livros". A classe deve estar com as anotações JPA para definir uma entidade. O atributo "livro" deve contér a anotação @OneToMany(mappedBy = "autor") para definir o relacionamento com a entidade "Livro" e a anotação "@JsonIgnore" para que quando seja feita a serialização para JSON, não ocorra uma loop infinito de chamadas.

  • Alteramos a classe "Livro", mudando o atributo livro de "String" para "Autor" e anotamos com @ManyToOne e @JoinColumn(name = "AUTOR_ID") para definir o relacionamento com a entidade "Autor".

  • Criamos a interface "AutoresRepository" definindo como um repositório para a entidade "Autor".

  • Criamos a classe "AutoresService" que será a camada de serviço e o método "listar" para listar todos os autores.

  • Criamos a classe "AutoresResource" e o método "listar" com o seguinte mapeamento: @RequestMapping(method = RequestMethod.GET).

Aula 2.17 - Evoluindo nosso recurso Autor

  • Criamos o método "salvar" na classe "AutoresResource" que deve salvar o Autor e devover o Http Status 201 - CREATED e informar o location do recurso criado utilizando "ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(autor.getId()).toUri();". Caso o Autor a ser cadastrado já exista ("AutorExistenteException"), deve retornar o Http Status 409 - CONFLICT.

  • Criamos o método "buscar" na classe "AutorResource" que deve busca um Autor através do Id informado. Caso o recurso não seja encontrado ("AutorNaoEncontradoException") a resposta HTTP será 404 - NOT_FOUND e caso seja encontrado será 200 - OK juntamente com os dados do recurso.

  • Ajustamos a classe "ResourceExceptionHandler" para manipular os novos erros emitidos pelo controller.

Aula 2.18 - Testando nossos recursos e formatando nossas representações JSON

  • Alteramos as classes de modelo inserindo a anotação @JsonFormat(pattern = "dd/MM/yyyy") para definir o formato da data a ser informada na entrada e como será seu formato na saída.

  • Alteramos a classe "Livro" anotando o atributo "comentarios" com anotação @JsonInclude(Include.NON_EMPTY) para evitar que a lista de comentários seja exibida na saída caso a mesma esteja nula.

Aula 2.19 - Validando as entradas na nossa API

  • Alteramos as classes "Autor", "Livro" e "Comentário" anotando os campos obrigatórios com @NotNull(message = "Campo obrigatório.").

  • Alteramos a classe "Livro" anotando o campo "resumo" com @Size(max = 1500, message = "O campo resumo excedeu o tamanho permitido. Permitido 1500 caracteres.")

  • Alteramos a classe "Comentario" anotando o campo "texto" com @Size(max = 1500, message = "O campo texto excedeu o tamanho permitido. Permitido 1500 caracteres.") e com @JsonProperty(value = "comentario") para mudar o campo na representação do recurso para "comentario".

Aula 2.20 - Negociação de conteúdos com Media Type

  • Alteramos o método "listar" na classe "AutoresResource" acrescentando na anotação @RequestMapping já existente o parâmetro: "produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}", ficando da seguinte forma: "@RequestMapping(method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})".

  • Em seguida adicionamos a seguinte dependência do Jackson para permitir produzir a sáída dos nossos serviços no formato XML:

      <dependency>
      	<groupId>com.fasterxml.jackson.dataformat</groupId>
      	<artifactId>jackson-dataformat-xml</artifactId>
      </dependency>
    
  • Para negociar o Media Type para o serviço, é necessário no header da requisição informar o atributo "Accept". Caso o valor seja "application/json" a representação do recurso será em JSON, caso seja "application/xml" a representação do recurso será em XML.

Aula 3.1 - Adicionando cache

  • Alteramos o método "buscar" da classe "LivrosResource" adicionando um controle de cache utilizando o seguinte código:
	CacheControl cacheControl = CacheControl.maxAge(20, TimeUnit.SECONDS);
	return ResponseEntity.status(HttpStatus.OK).cacheControl(cacheControl).body(livro);
  • Repare que criamos um objeto "CacheControl" e definimos que a informação valerá por 20 segundos. Passamos esse objeto na resposta da requisição. Com isso o cliente só voltará a buscar a informação no servidor após expirar o cache, ou seja, após 20 segundos.

  • No postman para verificar o funcionamento dessa funcionalidade será necessário desligar a opção "Send no-cache-header" que fica nas configurações do Postman.

Aula 3.2 - Autenticação com Spring Security

  • Vamos adicionar a dependencia do Spring Security no pom.xml

      <dependency>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-starter-security</artifactId>
      </dependency>			
    
  • Criamos a classe "WebSecurityConfig" que estende "WebSecurityConfigurerAdapter" (Spring Security) que será responsável por realizar as configurações de segurança da aplicação. A segurança será em memória (com um usuário e senha definidos na apliação), utilizando o "Basic Auth" (No header da requisição inserir a propriedade "Authorization" com valor "usuario:senha" na Base64). Exemplo: www.base64encode.org - admin:s3nh4

  • Para testar no Postman, clicar na opção "Authorization", selecionar o "Type" como "Basic Auth" e inserir o usuário e a senha.

Aula 4.1 - Automatizando testes com Postman

Aula 4.6 Cross-Origin Resource Sharing (CORS)

  • Adicionamos na configuração do Spring Security na classe "WebSecurityConfig" o seguinte código: ".antMatchers(HttpMethod.OPTIONS, "/**").permitAll()" o que permitirá que chamadas com método OPTIONS não seja autenticada pelo Spring Security

  • Adicionamos a anotação "@CrossOrigin" no método "listar" da classe "LivrosResources" para permitir o Cross Origin