API

O que são microsserviços?

O termo “microsserviços” tem sido muito utilizado nos últimos anos. Confira neste artigo o que é um microsserviço e quando utilizá-lo.

Afinal, o que vem a ser os microsserviços?

Microsserviços são uma abordagem de arquitetura para a criação de aplicações, onde cada pedaço dessa aplicação é desenvolvido e disponibilizado de forma independente. Cada processo da aplicação é executado como um serviço.

Quando falamos em microsserviços, estamos nos referindo a uma funcionalidade que pode ser dividida em partes menores. Desse modo, essas pequenas partes se comunicam por meio de uma interface bem definida usando APIs. Em outras palavras, como são executados de forma independente, cada serviço pode ser atualizado e implantado para atender às demandas de uma aplicação.

Uma arquitetura de microsserviços é um estilo moderno de arquitetura para web services, o que nos remete a outra arquitetura: a SOA. A SOA é uma alternativa à abordagem tradicional de construção de aplicações autossuficientes, as quais chamamos de monolíticos.

Se forem construídos corretamente, os serviços independentes não afetarão uns aos outros, ou seja: se um deles falhar, o restante da aplicação permanecerá em funcionamento, ao contrário do modelo monolítico.

O que são aplicações monolíticas e quais os problemas?

Ao contrário dos microsserviços, as aplicações monolíticas tem todas suas funcionalidades em um único processo, fazendo com que tenha um grande conjunto de funcionalidades em uma única estrutura, gerando alto acoplamento e indisponibilidade de toda a aplicação caso haja alterações ou implantação, mesmo que haja apenas um único ponto de falha.

Ainda assim, ele é interessante para aplicações pequenas, mas caso você esteja trabalhando com uma aplicação um pouco maior, alguns problemas podem surgir.

Quais são os benefícios da arquitetura de microsserviços?

A utilização de microsserviços pode trazer muita produtividade, pois você pode desenvolver vários microsserviços ao mesmo tempo, com diversos desenvolvedores trabalhando de forma simultânea na mesma aplicação com abordagens diferentes (como tecnologias envolvidas). Nesse sentido, temos nossas equipes bem focadas em suas tarefas, resultando em mais produtividade em menos tempo e um trabalho mais eficiente.

Flask - Fundamentos
Curso de Flask - Fundamentos
CONHEÇA O CURSO

Vantagens e desvantagens de se utilizar microsserviços

Vantagens
  • agilidade: com os deploys e testes independentes, temos uma maior agilidade no desenvolvimento e na implantação;
  • baixo acoplamento: com as aplicações são independentes, elas não possuem um acoplamento forte entre si. Isso facilita processos de manutenção, implantação e monitoramento;
  • escalabilidade flexível: cada serviço e funcionalidade pode ser escalado de maneira mais adequada e granular. Com isso, é possível obter até mesmo economia na manutenção da infraestrutura, pois o hardware pode ser escalado de maneira mais efetiva;
  • flexibilidade para implantação de tecnologias heterogêneas: como não existe acoplamento explícito e cada serviço é independente, podemos ter diferentes serviços escritos em diferentes tecnologias comunicando-se entre si. Isso permite utilizar linguagens mais adequadas para diferentes situações.
Desvantagens:
  • Complexidade na implementação e operação de sistemas distribuídos: sistemas distribuídos trazem complexidades adicionais, como monitoramento, consistência e distribuição de transações, pulverização de fontes de dados etc.;
  • Testes: testar uma aplicação com muitas dependências de serviços pode ser desafiador.

Quando deve-se considerar utilizar essa abordagem?

É preciso entender quando e como usar corretamente uma arquitetura orientada a microserviços. Pontos como complexidade do negócio, tamanho da equipe e distribuição das atividades são pontos relevantes a serem considerados. Caso contrário, a chance da adoção de uma arquitetura orientada a microserviços fracassar cresce consideravelmente.

Por isso, alguns casos que devem ser levado em conta ao utilizar a abordagem de microsserviços é em aplicações grandes e complexas. Essas aplicações geralmente precisam de uma alta taxa de velocidade de liberação, além de precisarem ser altamente escaláveis e disponíveis.

Considerações finais…

Os microsserviços são parte importante da estratégia de desenvolvimento de projetos distribuídos e altamente escaláveis. A utilização de microsserviços facilitam a escalabilidade e na agilidade do desenvolvimento, acelerando o tempo de introdução de novos recursos.

O que é HATEOAS?

HATEOAS é uma restrição que faz parte da arquitetura de aplicações REST, cujo objetivo é ajudar os clientes a consumirem o serviço sem a necessidade de conhecimento prévio profundo da API.

O acrônimo HATEOAS vem de Hypermedia As the Engine Of Application State e o termo “hypermedia” no seu nome já dá uma ideia de como este componente funciona em uma aplicação RESTful. Ao ser implementado, a API passa a fornecer links que indicarão aos clientes como navegar através dos seus recursos.

Com isso, o cliente não precisa ter um conhecimento profundo da API, basta conhecer a URL de inicial e partir dos links fornecidos poderá acessar todos os recursos de forma circular, se guiando através das requisições realizadas.

Um exemplo clássico para explicar o HATEOAS, é o Hypertext, do HTML. Quando se necessita obter uma informação de um site, como um curso aqui da Treinaweb, o usuário pode acessar a página inicial (treinaweb.com.br), nela clicar em Cursos e depois no curso desejado. No fim, o usuário terá acessado uma URL como treinaweb.com.br/curso/java-fundamentos-de-jax-ws-e-jax-rs, onde conseguirá visualizar o curso. Não foi necessário que o usuário soubesse de antemão a URL desejada, pois é importante que o site, através de links, guie-o e auxilie-o na busca do recurso desejado. Com o HATEOAS uma API REST segue o mesmo padrão.

Um exemplo simples do HATEOAS

Por exemplo, ao acessar uma API de cursos, na URL api.treinaweb.com.br/cursos, o cliente terá o seguinte resultado:

{
    "cursos": [
        {
            "id": 1,
            "nome": "C# (C Sharp)",
            "aulas": [
                {
                    "id": 1,
                    "titulo": "Título da aula 3"
                },
                {
                    "id": 2,
                    "titulo": "Título da aula 3"
                },
                {
                    "id": 3,
                    "titulo": "Título da aula 3"
                },
            ]
        },
        {
            "id": 2,
            "nome": "PHP",
            "aulas": [
                {
                    "id": 1,
                    "titulo": "Título da aula 3"
                },
                {
                    "id": 2,
                    "titulo": "Título da aula 3"
                },
                {
                    "id": 3,
                    "titulo": "Título da aula 3"
                },
            ]
        },
        {
            "id": 3,
            "nome": "Java",
            "aulas": [
                {
                    "id": 1,
                    "titulo": "Título da aula 3"
                },
                {
                    "id": 2,
                    "titulo": "Título da aula 3"
                },
                {
                    "id": 3,
                    "titulo": "Título da aula 3"
                },
            ]
        },
    ]
}

Se esta API implementasse o padrão HATEOAS, a resposta obtida poderia ser outra. No lugar de listar as aulas, cada curso teria um recurso próprio que retornaria suas aulas. Evitando assim que elas fiquem expostas do corpo da requisição. Algo como o exemplo abaixo:

{
    "cursos": [
        {
            "id": 1,
            "nome": "C# (C Sharp)",
            "aulas": "api.treinaweb.com.br/cursos/1/aulas"
        },
        {
            "id": 2,
            "nome": "PHP",
            "aulas": "api.treinaweb.com.br/cursos/2/aulas"
        },
        {
            "id": 3,
            "nome": "Java",
            "aulas": "api.treinaweb.com.br/cursos/3/aulas"
        },
    ]
}

Desta forma, se o cliente desejasse obter as aulas do curso de PHP, em seguida acessaria a URL api.treinaweb.com.br/cursos/2/aulas. Note que nem é necessário montar a URL, a API já fornece o caminho correto completo.

Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Especificação do HATEOAS

No exemplo anterior, o HATEOAS foi exemplificado como uma URL na resposta da API, entretanto, na prática, recomenda-se que a implementação da HATEOAS siga um dos padrões abaixo:

RFC 5988

A especificação RFC 5988 da IETF define como links devem ser implementados. De acordo com ela, cada link deve ter as informações:

  • URI: Cada link deve conter uma URI, representada no atributo href;
  • Tipo de relação: Descreve como a URI se relaciona com o recurso atual, representado pelo atributo rel, devidado de relationship;
  • Atributos para URI: Para descrever melhor a URI podem ser adicionados atributos como: hreflang, media, title e type.

JSON Hypermedia API Language (HAL)

JSON HAL é uma especificação, proposta por Mike Kelly, ainda não aprovada, mas já comumente utilizada, que define dois MIME Types:

application/hal+xml
application/hal+json

Que ao serem enviados na solicitação, a API REST deve retornar uma propriedade links, contendo as informações:

  • URI: A URI do recurso, representada pelo atributo href;
  • Tipo de relação: Descreve como a URI se relaciona com o recurso atual, representado pelo atributo rel;
  • Tipo: Descreve o tipo de conteúdo obtido ou do tipo de verbo que deve ser utilizado para acessar a URI. Representado pelo atributo type.

Desta forma, na prática, uma API REST que implemente HATEOAS retornar uma resposta como a abaixo:

{
    "cursos": [
        {
            "id": 1,
            "nome": "C# (C Sharp)",
            "links": [
                {
                    "type": "GET",
                    "rel": "self",
                    "uri": "api.treinaweb.com.br/cursos/1"
                },
                {
                    "type": "GET",
                    "rel": "curso_aulas",
                    "uri": "api.treinaweb.com.br/cursos/1/aulas"
                },
                {
                    "type": "PUT",
                    "rel": "curso_atualizacao",
                    "uri": "api.treinaweb.com.br/cursos/1"
                },
                {
                    "type": "DELETE",
                    "rel": "curso_exclusao",
                    "uri": "api.treinaweb.com.br/cursos/1"
                }
            ]
        },
        {
            "id": 2,
            "nome": "PHP",
            "links": [
                {
                    "type": "GET",
                    "rel": "self",
                    "uri": "api.treinaweb.com.br/cursos/2"
                },
                {
                    "type": "GET",
                    "rel": "curso_aulas",
                    "uri": "api.treinaweb.com.br/cursos/2/aulas"
                },
                {
                    "type": "PUT",
                    "rel": "curso_atualizacao",
                    "uri": "api.treinaweb.com.br/cursos/2"
                },
                {
                    "type": "DELETE",
                    "rel": "curso_exclusao",
                    "uri": "api.treinaweb.com.br/cursos/2"
                }
            ]
        },
        {
            "id": 3,
            "nome": "Java",
            "links": [
                {
                    "type": "GET",
                    "rel": "self",
                    "uri": "api.treinaweb.com.br/cursos/3"
                },
                {
                    "type": "GET",
                    "rel": "curso_aulas",
                    "uri": "api.treinaweb.com.br/cursos/3/aulas"
                },
                {
                    "type": "PUT",
                    "rel": "curso_atualizacao",
                    "uri": "api.treinaweb.com.br/cursos/3"
                },
                {
                    "type": "DELETE",
                    "rel": "curso_exclusao",
                    "uri": "api.treinaweb.com.br/cursos/3"
                }
            ]
        }
    ]
}

Vantagens do HATEOAS

Uma das principais vantagens do HATEOAS é permitir representar o estado de um recurso ou as limitações de um usuário sem a necessidade da implementação de outras lógicas no cliente.

Por exemplo, imagine uma API de um banco. Ao consultar o saldo do usuário, caso este tenha saldo em conta, pode ser retornado os seguintes dados:

{
    "conta": {
        "numero": 1234,
        "saldo": 5120.09,
        "links": 
        [
            {
                "type": "GET",
                "rel": "self",
                "uri": "api.banco.com.br/usuario/1/conta/1234"
            },
            {
                "type": "PUT",
                "rel": "conta_saque",
                "uri": "api.banco.com.br/usuario/1/conta/1234/saque"
            },
            {
                "type": "PUT",
                "rel": "conta_deposito",
                "uri": "api.banco.com.br/usuario/1/conta/1234/deposito"
            }
        ]
    }
}

Caso não tenha saldo, o resultado pode mudar para:

{
    "conta": {
        "numero": 1234,
        "saldo": -20.14,
        "links": 
        [
            {
                "type": "GET",
                "rel": "self",
                "uri": "api.banco.com.br/usuario/1/conta/1234"
            },
            {
                "type": "PUT",
                "rel": "conta_deposito",
                "uri": "api.banco.com.br/usuario/1/conta/1234/deposito"
            }
        ]
    }
}

Desta forma, o cliente não precisa se preocupar com o estado do usuário. Ele saberá que os ações disponíveis serão as retornadas no atributo links.

Outra vantagem é a prevenção do chamado “hardcoding”, quando informações são inseridas diretamente no código da aplicação. Em todos os nossos exemplos vimos que o cliente precisa conhecer apenas a URL base da API, pois esta já irá retornar as demais URLs disponíveis.

Por este comportamento que o HATEOAS geralmente é aplicado em aplicações de proposito mais geral, que são criadas para sobreviverem por um longo período. Onde alterações na API não irão impactar muito nos clientes.

C# (C Sharp) - APIs REST com ASP.NET Web API
Curso de C# (C Sharp) - APIs REST com ASP.NET Web API
CONHEÇA O CURSO

Conclusão

De acordo com o modelo de maturidade de Richardson, o HATEOAS é considerado o último nível de uma API RESTful. Desta forma, caso esteja procurando definir uma API que siga o padrão RESTful, o HATEOAS deve ser implementado nela.

Mas mesmo que não esteja seguindo o padrão RESTful a risca, é fato que o componente HATEOAS facilita e muito a manutenção de uma API e a sua integração com outras aplicações. Então, sempre que possível procure implementá-la nas APIs que desenvolver.

Criando uma API RESTful com a JAX-RS API

No Java, quando pensamos na criação de uma API RESTful a primeira opção que vem a mente é o projeto Spring. Este projeto facilita a criação de APIs REST com o Spring Boot, que é uma das bibliotecas fornecidas por ele. Entretanto, o Java define uma especificação para este tipo de aplicação, a JAX-RS API.

Mesmo não possuindo tanto destaque quanto o Spring Boot, como veremos neste artigo, é bem simples criar uma API REST com a JAX-RS API.

Java - Fundamentos de JAX-WS e JAX-RS
Curso de Java - Fundamentos de JAX-WS e JAX-RS
CONHEÇA O CURSO

A especificação JAX-RS API

A JAX-RS API trata-se de uma especificação, ela define interfaces e anotações fornecidas pelo Java EE, que podem ser utilizadas na criação de uma API RESTful. Um desenvolvedor pode criar a sua aplicação baseada unicamente nesta especificação, entretanto o usual é fazer uso de uma biblioteca que a implemente.

Entre as bibliotecas existentes, as implementações da JAX-RS API mais conhecidas são RESTEasy e Jersey. Sendo que a mais utilizada é a Jersey, assim neste artigo faremos uso dela.

Então vamos colocar a mão na massa.

Criando a aplicação

Para este artigo irei criar um projeto Maven utilizando o arquétipo jersey-quickstart-grizzly2, pois este arquétipo já possui o Jersey configurado e fornece embutido o servidor Grizzly.

Pelo terminal, o projeto pode ser criado com o comando abaixo:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=br.com.treinaweb -DartifactId=jaxrsexample -Dpackage=br.com.treinaweb

No arquivo pom.xml haverão as dependências do Jersey:

<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.inject</groupId>
  <artifactId>jersey-hk2</artifactId>
</dependency>

Caso não queira utilizar o Jersey com o Grizzly, você pode substituir a primeira dependência acima pelas abaixo:

<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-server</artifactId>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-servlet</artifactId>
</dependency>

Com o projeto criado e o Jersey configurado, vamos começar a criar a nossa API REST com JAX-RS API.

Criando uma entidade e repositório

Este exemplo será um CRUD simples, mas para isso necessitamos de uma entidade, que será representada pela classe abaixo:

public class Pessoa {
    private int id;
    private String nome;
    private int idade;

    public int getId() {
        return id;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

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

Esta aplicação não lidará com um banco de dados, assim o repositório utilizará um hashmap na memória:

public class PessoaRepository {
    private final static HashMap<Integer, Pessoa> pessoas = new HashMap<>();

    public List<Pessoa> GetAll(){
        return new ArrayList<Pessoa>(pessoas.values());
    }

    public Pessoa Get(final int id) {
        return pessoas.get(id);
    }

    public void Add(final Pessoa pessoa) {
        if(pessoa.getId() == 0 )
            pessoa.setId(generateId(pessoas.size() + 1));
        pessoas.put(pessoa.getId(), pessoa);
    }

    public void Edit(final Pessoa pessoa) {
        pessoas.remove(pessoa.getId());
        pessoas.put(pessoa.getId(), pessoa);
    }

    public void Delete(final int id) {
        pessoas.remove(id);
    }

    private int generateId(final int possible)
    {
        if(pessoas.containsKey(possible))
            return generateId(possible + 1);
        return possible;
    }
}

Agora podemos criar o nosso recurso.

Recursos

No Jersey os endpoints são chamados de recursos. Um recurso nada mais é que uma classe que contém a anotação @Path:

import javax.ws.rs.Path;

@Path("/pessoa")
public class PessoaResource {

}

Como é possível notar no código acima, a anotação @Path é utilizada para indicar um caminho. Ao defini-lo na classe, significa que este será o nosso endpoint. Dentro dela, definimos métodos que representam os verbos HTTP, onde cada método deverá conter a anotação equivalente ao verbo que o invocará:

  • @DELETE = DELETE;
  • @GET = GET;
  • @HEAD = HEAD;
  • @OPTIONS = OPTIONS;
  • @POST = POST;
  • @PUT = PUT;
  • @PATCH = PATCH.

Neste nosso endpoint não utilizaremos todas essas anotações, apenas as equivalentes aos principais verbos: DELETE, GET, POST e PUT.

Ao realizar este procedimento, a classe ficará com o seguinte código:

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import br.com.treinaweb.model.Pessoa;
import br.com.treinaweb.repositories.PessoaRepository;

@Path("/pessoa")
public class PessoaResource {

    private PessoaRepository _repositorio = new PessoaRepository();

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Pessoa> get() {
        return _repositorio.GetAll();
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Pessoa getById(@PathParam("id") int id) {
        return _repositorio.Get(id);
    }

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(Pessoa pessoa)
    {
        try{
            _repositorio.Add(pessoa);
            return Response.status(Response.Status.CREATED).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }

    @PUT
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response put(@PathParam("id") int id, Pessoa pessoa)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            pessoa.setId(id);
            _repositorio.Edit(pessoa);
            return Response.status(Response.Status.OK).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }

    @DELETE
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response delete(@PathParam("id") int id)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            _repositorio.Delete(id);
            return Response.status(Response.Status.OK).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }
}

Repare que os métodos também definem o tipo de conteúdo que irão produzir/retornar:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Pessoa> get() {
  return _repositorio.GetAll();
}

E o tipo que irão consumir:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response post(Pessoa pessoa)
{
  try{
    _repositorio.Add(pessoa);
    return Response.status(Response.Status.CREATED).entity(pessoa).build();
  }
  catch(Exception ex)
  {
    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
  } 
}

O Jersey se encarregará de converter o objeto retornado para JSON e o JSON recebido para o tipo do objeto indicado. Mas para que esta a conversão seja possível, é necessário definir a dependência abaixo (caso não esteja definida):

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-binding</artifactId>
</dependency>

Pronto, a nossa aplicação já pode ser utilizada.

Testando a aplicação

Caso esteja acompanhando este artigo e se o seu projeto foi criado com base o arquetipo jersey-quickstart-grizzly2, você pode compilar os códigos pelo Maven:

mvn clean compile

E executar o servidor embutido da aplicação:

mvn exec:java

Indiferente da forma que a aplicação é executada, poderemos interagir com o nosso endpoint:

POST:

Tela no Postman, mostrando o exemplo de uma requisição POST para o endpoint "http://localhost:8080/pessoa"

GET:

Tela no Postman, mostrando o exemplo de uma requisição GET para o endpoint "http://localhost:8080/pessoa"

PUT:

Tela no Postman, mostrando o exemplo de uma requisição PUT para o endpoint "http://localhost:8080/pessoa/1

GET ID:

Tela no Postman, mostrando o exemplo de uma requisição GET para o endpoint "http://localhost:8080/pessoa/1"

DELETE:

Tela no Postman, mostrando o exemplo de uma requisição DELETE para o endpoint "http://localhost:8080/pessoa/1"

Java - Introdução à JPA
Curso de Java - Introdução à JPA
CONHEÇA O CURSO

Conclusão

A JAX-RS API é uma poderosa especificação que pode ser aplicada em uma aplicação facilmente graças a implementação do Jersey, se tornando uma ótima alternativa para o Spring Boot.

Desta forma, caso necessite criar uma aplicação RESTfull API no Java, não deixe de dar uma olhada nos recursos fornecidos pela JAX-RS API e o Jersey.

Você pode baixar a aplicação deste artigo no meu Github.

Vídeos Picture-in-Picture com JavaScript

Olá Web Developers! Hoje vamos ver como permitir o usuário assistir a um vídeo da sua página web fora do navegador.

O que é PIP?

Picture-in-Picture (PIP), também conhecida aqui como Imagem sobre Imagem, é quando temos a imagem de uma tela sendo exibida em cima de outra imagem.

Um exemplo são monitores para computador que também possuem funcionalidade de TV. Com PIP você pode trabalhar e ao mesmo tempo deixar a imagem da TV no canto da tela.

Monitor - TV - PIP

PIP no HTML5

A API de Picture-in-Picture permite que páginas da web peguem um vídeo qualquer presente em seu corpo e o exiba em uma pequena janela flutuante que ficará sempre em cima das outras janelas. Assim o usuário poderá continuar assistindo enquanto faz outras tarefas.

Na imagem abaixo estamos com um vídeo comum no YouTube:

Ao ativar o PIP o vídeo passa a ser reproduzido em uma janela flutuante que você pode mover e redimensionar. Mesmo que você abra outras janelas, o vídeo continua em cima de todas as outras janelas.

Na imagem abaixo vemos um exemplo de navegação em nosso blog enquanto o vídeo do YouTube permanece visível. Claro que você pode minimizar as janelas do navegador e abrir qualquer outro programa.

Esta funcionalidade está sendo adotada também em smartphones, o que nos permite continuar assistindo a um vídeo do navegador enquanto utilizamos outros aplicativos.

Como iniciar o PIP?

Para iniciar o Picture-in-Picture é necessário acessar o elemento HTML <video> que está com o vídeo que você quer que flutue. Imagine que a gente tenha o seguinte HTML:

<button onclick="iniciarPIP()">Picture in Picture</button>

<video src="./meu-video.mp4" id="meu-video" controls></video>

Criamos um botão para iniciar o PIP e adicionamos um vídeo. Agora basta pegar o elemento <video> e executar o método .requestPictureInPicture(). Este método retorna uma Promise.

async function iniciarPIP(){
    const video = document.querySelector('#meu-video');
    await video.requestPictureInPicture();
}

Você pode ver o resultado abaixo:

Verificando a integridade da aplicação ASP.NET Core com Health Checks

Alguns artigos passados falei sobre as ferramentas de linha de comando que permitem diagnosticar uma aplicação no .NET Core 3.0. Por serem ferramentas de linha de comando, elas requerem acesso a máquina onde a aplicação está instalada. Infelizmente em muitos cenários algo assim não é possível, mas o monitoramento do status da aplicação não deixa de ser importante nesses casos.

Se tratando de aplicações ASP.NET, antigamente o comum era criar um endpoint que retornasse o estado da aplicação ou menos nada era definido. Mas com a adoção cada vez maior de microsserviços e a criação de aplicações para um ambiente distribuído, implementar a verificação da integridade da aplicação se tornou algo essencial.

C# (C Sharp) Básico
Curso de C# (C Sharp) Básico
CONHEÇA O CURSO

Felizmente, a partir da versão 2.2 do ASP.NET Core, foi introduzido os Health Checks que facilitam este trabalho.

Conhecendo os Health Checks

Health check é um middleware que fornece um endpoint que retorna o status da aplicação. Na sua versão básica, a aplicação é considerada saudável caso retorne o código 200 (OK) para uma solicitação web. Mas também são fornecidas bibliotecas que nos permite verificar o status de serviços utilizados pela aplicação, como: banco de dados, sistema de mensageria, cache, logging, serviços externos ou mesmo a criação de um health check customizado.

Aplicação que terá a integridade verificada

Para exemplificar o uso do health check, vou utilizar uma aplicação ASP.NET Core Web API simples, que já foi mostrada no artigo sobre a documentação de uma ASP.NET Core Web API com o Swagger, que você pode ver no meu Github.

Adicionando o Health Check na aplicação

Agora que já temos a aplicação, já podemos ativar o health check nela. Para isso, basta adicionar o service no método ConfigureServices da classe Startup e indicar a URL dele no método Configure:

public void ConfigureServices(IServiceCollection services)
{
    //....
    services.AddHealthChecks();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...

    //Ativa o HealthChecks
    app.UseHealthChecks("/status");
    //...
}

Agora, ao executar a aplicação e acessar a URL /status será exibido se ela está saudável ou não:

Como está sendo testado apenas se ela responde a uma requisição, a aplicação é considerada saudável.

Integridade do banco de dados

Existem várias formas de testar a integridade do banco de dados. É fornecido uma gama de pacotes que nos permite testar a integridade do banco de acordo com o SGBD:

  • SQL Server: AspNetCore.HealthChecks.SqlServer;
  • MySQL: AspNetCore.HealthChecks.MySql;
  • PostgreSQL: AspNetCore.HealthChecks.Npgsql;
  • SQLite: AspNetCore.HealthChecks.SqLite;
  • Oracle: AspNetCore.HealthChecks.Oracle;
  • MongoDb: AspNetCore.HealthChecks.MongoDb.

Caso esteja trabalhando com o Entity Framework, também há o pacote Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore, que será o que utilizaremos na aplicação:

dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore

Com isso, basta adicionar a classe DbContext, que o estado da conexão com o banco será verificada:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddHealthChecks()
            .AddDbContextCheck<TodoContext>();
}

Se o banco estiver saudável, o resultado não será alterado:

Caso haja algum problema com a conexão com o banco, a aplicação não será considerada saudável:

Alterando as informações exibidas

No momento, somos informados apenas se a aplicação está ou não saudável. Para uma aplicação pequena, isso pode ser útil, mas imagine uma aplicação que verifica vários serviços? Apenas com esta informação não conseguiremos saber qual serviço não está funcionando corretamente.

Para obter esses dados, podemos customizar as informações de saída do health check, definindo um delegate na opção ResponseWriter:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...

    //Ativa o HealthChecks
    app.UseHealthChecks("/status", new HealthCheckOptions()
    {
        // WriteResponse é um delegate que permite alterar a saída.
        ResponseWriter = (httpContext, result) => {
            httpContext.Response.ContentType = "application/json";

            var json = new JObject(
                new JProperty("status", result.Status.ToString()),
                new JProperty("results", new JObject(result.Entries.Select(pair =>
                    new JProperty(pair.Key, new JObject(
                        new JProperty("status", pair.Value.Status.ToString()),
                        new JProperty("description", pair.Value.Description),
                        new JProperty("data", new JObject(pair.Value.Data.Select(
                            p => new JProperty(p.Key, p.Value))))))))));
            return httpContext.Response.WriteAsync(json.ToString(Formatting.Indented));
        }
    });
    //...
}

Com isso, obteremos a causa do problema da aplicação:

Criando health checks customizados

Existe uma grande gama de health checks definidos, mas em algumas situações, pode ser necessário definir um customizado. Para isso, é necessário definir uma classe que implemente a interface IHealthCheck e definir no método CheckHealthAsync qual tipo de verificação ele fará:

public class SelfHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        return Task.FromResult(new HealthCheckResult(
            HealthStatus.Healthy,
            description: "API up!"));
    }
}

Acima é definido um health check simples, que apenas indica que a API está funcionando.

C# (C Sharp) Intermediário
Curso de C# (C Sharp) Intermediário
CONHEÇA O CURSO

Agora é necessário adicioná-lo ao serviço com o método AddCheck:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddHealthChecks()
            .AddDbContextCheck<TodoContext>()
            .AddCheck<SelfHealthCheck>("Self");
}

Também pode ser definido um método de extensão:

public static class HealthCheckBuilderExtensions
{
    public static IHealthChecksBuilder AddSelfCheck(this IHealthChecksBuilder builder, string name, HealthStatus? failureStatus = null, IEnumerable<string> tags = null)
    {
        // Register a check of type SelfHealthCheck
        builder.AddCheck<SelfHealthCheck>(name, failureStatus ?? HealthStatus.Degraded, tags);

        return builder;
    }
}

E utilizá-lo para registrar o health check:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddHealthChecks()
            .AddDbContextCheck<TodoContext>()
            .AddSelfCheck("Self");
}

Ao acessar a aplicação, ele também será mostrado:

Adicionando uma interface

No momento o resultado do status da aplicação é um JSON, mas existe um pacote que nos permite visualizar as informações em uma interface gráfica. Para isso, adicione na aplicação os pacotes AspNetCore.HealthChecks.UI e AspnetCore.HealthChecks.UI.Client:

dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspnetCore.HealthChecks.UI.Client

Em seguida é necessário habilitar o Health Checks UI nos métodos ConfigureServices e Configure da classe Startup:

public void ConfigureServices(IServiceCollection services)
{
    //....
    services.AddHealthChecks()
            .AddDbContextCheck<TodoContext>()
            .AddSelfCheck("Self");

    services.AddHealthChecksUI();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...

    //Ativa o HealthChecks
    app.UseHealthChecks("/status", new HealthCheckOptions()
    {
        // WriteResponse is a delegate used to write the response.
        ResponseWriter = (httpContext, result) => {
            httpContext.Response.ContentType = "application/json";

            var json = new JObject(
                new JProperty("status", result.Status.ToString()),
                new JProperty("results", new JObject(result.Entries.Select(pair =>
                    new JProperty(pair.Key, new JObject(
                        new JProperty("status", pair.Value.Status.ToString()),
                        new JProperty("description", pair.Value.Description),
                        new JProperty("data", new JObject(pair.Value.Data.Select(
                            p => new JProperty(p.Key, p.Value))))))))));
            return httpContext.Response.WriteAsync(json.ToString(Formatting.Indented));
        }
    });

    //Ativa o HealthChecks utilizado pelo HealthCheckUI
    app.UseHealthChecks("/status-api", new HealthCheckOptions()
    {
        Predicate = _ => true,
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });

    app.UseHealthChecksUI(opt => {
        opt.UIPath = "/status-dashboard";
    });

    //...
}

Além de habilitar o Health Checks UI note que também é definido um novo endpoint para o health check, configurado para o delegate UIResponseWriter.WriteHealthCheckUIResponse. Este é um delegate criado pelo Health Checks UI, e configura um arquivo JSON que será lido pela biblioteca.

Para que ele seja lido, é necessário especificá-lo no arquivo appsettings.json:

{
  "HealthChecks-UI": {
    "HealthChecks": [
      {
        "Name": "Status-API",
        "Uri": "https://localhost:5001/status-api"
      }
    ]
  }
}

Com isso, ao executar a aplicação e acessar a URL /status-dashboard a interface será exibida:

Por hoje é isso 🙂

C# (C Sharp) Avançado
Curso de C# (C Sharp) Avançado
CONHEÇA O CURSO

Lembrando que você pode ver o código da aplicação no Github.

Documentando uma API Spring Boot com o Swagger

Documentar uma aplicação é um ponto essencial de qualquer projeto, muitas vezes negligenciado. Quando se trabalha em equipe, uma má documentação pode dificultar (e muito) o trabalho dos demais desenvolvedores.

Por já ter muita dificuldade com isso, procuro sempre documentar as aplicações que estou trabalhando. No ano passado, mostrei como documentar uma aplicação ASP.NET Core Web API com o Swagger. Por gostar muito dele, nesta semana quando precisei documentar API Spring Boot, não tive dúvidas que ele é era a escolha correta.

A partir da experiência que tive neste processo, aqui vou lhe mostrar como documentar uma API Spring Boot com o Swagger.

Relembrando características do Swagger

Caso não tenha visto o meu artigo de ASP.NET Core Web API com o Swagger, vamos relembrar alguns detalhes do Swagger:

  • O Swagger é uma aplicação open source que auxilia os desenvolvedores a definir, criar, documentar e consumir APIs REST;
  • É composto de um arquivo de configuração, que pode ser definido em YAML ou JSON;
  • Fornece ferramentas para: auxiliar na definição do arquivo de configuração (Swagger Editor), interagir com API através das definições do arquivo de configuração (Swagger UI) e gerar templates de código a partir do arquivo de configuração (Swagger Codegen).

Como é possível notar, o ponto mais importante é o arquivo de configuração do Swagger. É nele que a API é documentada.

Criar este arquivo na mão pode ser um trabalho hercúleo, felizmente existem algumas bibliotecas do Java que facilitam este processo. No caso de uma aplicação Spring, a melhor opção é a biblioteca SpringFox.

Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Adicionando a biblioteca SpringFox em uma aplicação API Spring Boot

Para este artigo, estou utilizando a aplicação mostrada no meu último artigo, onde ensinei a criar uma aplicação REST API no Spring Boot. Você também pode ver a aplicação que criei no meu Github.

Com a aplicação criada, temos que adicionar a dependência do SpringFox nela:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

Para interagir com a configuração, também é necessário adicionar a dependência que fornece o Swagger UI:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Agora para que o arquivo de especificação da API seja criado, é necessário habilitar o Swagger na aplicação. Para isso, adicione nela uma classe chamada SwaggerConfig, com o conteúdo abaixo:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
          .select()
          .apis(RequestHandlerSelectors.any())
          .paths(PathSelectors.any())
          .build();
    }
}

No Spring Boot o Swagger é ativado através da anotação @EnableSwagger2. O Docket que estamos definindo no nosso bean nos permite configurar aspectos dos endpoints expostos por ele.

Nos métodos apis() e paths() definimos que todas as apis e caminhos estarão disponíveis. Com isso através de reflection a biblioteca já consegue obter os endpoints definidos na aplicação.

Ao executá-la, o Swagger UI estará disponível em /swagger-ui.html:

Note que ele está pegando informações de todos os controllers definidos na aplicação até o de erro padrão do Spring Boot. Caso queria evitar isso, é possível configurar o Swagger.

Customizando o Swagger na aplicação Spring Boot

Todas as configurações do Swagger devem ser definidas na classe SwaggerConfig. No momento ela contém apenas as configurações padrão.

Indicando código e mensagem de retorno do Swagger

Nas configurações padrão, o Swagger irá indicar que os endpoints retornam os códigos 200, 201, 204, 401, 403 e 404. Caso a sua aplicação não retorne todos esses códigos, você pode especificar quais códigos ela retorna com o método globalResponseMessage.

O globalResponseMessage recebe por parâmetro o método HTTP e uma lista de ResponseMessage que indica quais códigos e mensagens o método retorna. Para que a aplicação fique modular, vamos definir os ResponseMessage em um método:

private List<ResponseMessage> responseMessageForGET()
{
    return new ArrayList<ResponseMessage>() {{
        add(new ResponseMessageBuilder()
            .code(500)
            .message("500 message")
            .responseModel(new ModelRef("Error"))
            .build());
        add(new ResponseMessageBuilder()
            .code(403)
            .message("Forbidden!")
            .build());
    }};
}

E ele será passado como parâmetro do globalResponseMessage:

public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.any())
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}

Agora, ficará indicado que todos os endpoints GET retornam o código 200 (que é sempre padrão) e os códigos 500 e 403:

Note na imagem acima, que em “Response content type” é indicado o valor /. Para alterar este ponto, basta indicar no seu endpoint o tipo do conteúdo que ele produz com o atributo produces:

@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
    return _pessoaRepository.findAll();
}

Com o atributo consumes é possível especificar o tipo de conteúdo que ele consome:

@RequestMapping(value = "/pessoa", method =  RequestMethod.POST, produces="application/json", consumes="application/json")
public Pessoa Post(@Valid @RequestBody Pessoa pessoa)
{
    return _pessoaRepository.save(pessoa);
}

Também é possível especificar os códigos e as mensagens de retorno diretamente no controller com as anotações @ApiResponses e @ApiResponse:

@ApiResponses(value = {
    @ApiResponse(code = 200, message = "Retorna a lista de pessoa"),
    @ApiResponse(code = 403, message = "Você não tem permissão para acessar este recurso"),
    @ApiResponse(code = 500, message = "Foi gerada uma exceção"),
})
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
    return _pessoaRepository.findAll();
}

Estas configurações serão válidas para os endpoints onde estiverem definidos. Onde não estiver, será utilizado o padrão.

Também é possível utilizar a anotação @ApiOperation para descrever o endpoint:

@ApiOperation(value = "Retorna uma lista de pessoas")
@ApiResponses(value = {
    @ApiResponse(code = 200, message = "Retorna a lista de pessoa"),
    @ApiResponse(code = 403, message = "Você não tem permissão para acessar este recurso"),
    @ApiResponse(code = 500, message = "Foi gerada uma exceção"),
})
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
    return _pessoaRepository.findAll();
}

E caso queira descrever o model, pode ser utilizado a anotação @ApiModelProperty:

@Entity
public class Pessoa
{
    @ApiModelProperty(value = "Código da pessoa")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ApiModelProperty(value = "Nome da pessoa")
    @Column(nullable = false)
    private String nome;

    public long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public void setId(long id) {
        this.id = id;
    }
}
Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Filtrando os endpoints da API Spring Boot no Swagger

No momento o Swagger está listando todos os endpoints definidos na aplicação, inclusive os padrões do Spring Boot, como o de erro. Mas na configuração dele, no método apis podemos utilizar a classe RequestHandlerSelectors para filtrar quais serão considerados com base no pacote ou anotação. Por exemplo, para que seja listada apenas os endpoints definidos pela nossa aplicação, utilizamos o método basePackage() desta classe:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
       .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}

Com a classe PathSelectors, também é possível filtrar os caminhos aceitos para os endpoints, como:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
       .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.ant("/api/*"))
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}

Acima estamos utilizando o método ant() para adicionar o filtro, mas também seria possível definir uma expressão regular com o método regex().

Especificando a autenticação da aplicação no Swagger

Uma das principais vantagens da Swagger UI é a possibilidade de testar os endpoints diretamente pela interface. Quando a aplicação define alguma autenticação, é necessário configurar isso, para que o SpringFox também especifique isso, e mesmo endpoints protegidos sejam testáveis.

Esta especificação é realizada com os métodos securitySchemes e securityContexts. No primeiro é definido o tipo de autenticação (no momento os suportados são: ApiKey, BasicAuth e OAuth). Já no segundo são especificadas particularidades desta autenticação, como os escopos e endpoints que necessitam de autenticação.

No caso deste exemplo, será definida uma autenticação ApiKey:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET())
        .securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name())))
        .securityContexts(Arrays.asList(securityContext()));
}

Já o securityContext() conterá o código abaixo:

private SecurityContext securityContext() {
    return SecurityContext.builder()
        .securityReferences(defaultAuth())
        .forPaths(PathSelectors.ant("/pessoa/**"))
        .build();
}

List<SecurityReference> defaultAuth() {
    AuthorizationScope authorizationScope
        = new AuthorizationScope("ADMIN", "accessEverything");
    AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
    authorizationScopes[0] = authorizationScope;
    return Arrays.asList(
        new SecurityReference("Token Access", authorizationScopes));
}

Onde é definido que os endpoint de /pessoa necessitam de autenticação.

Agora ao executar a aplicação e acessar o Swagger, haverá o botão “Authorize”:

Ao clicar nele, será exibido a tela onde a forma de acesso deve ser informada:

Na aplicação deste exemplo, a autenticação é TOKEN, então ao informá-lo e clicar em Authorize, será possível realizar solicitações nos endpoints que necessitam de autenticação.

Descrevendo a API no Swagger

Por fim, é possível adicionar algumas informações da api no método apiInfo:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET())
        .securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name())))
        .securityContexts(Arrays.asList(securityContext()))
        .apiInfo(apiInfo());
}

private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
            .title("Simple Spring Boot REST API")
            .description(""Um exemplo de aplicação Spring Boot REST API"")
            .version("1.0.0")
            .license("Apache License Version 2.0")
            .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0"")
            .contact(new Contact("Wladimilson", "https://treinaweb.com.br", "contato@treinaweb.com.br"))
            .build();
}

O resultado será:

Você pode ver a aplicação deste artigo no meu Github.

Devo usar o Swagger em uma aplicação API Spring Boot?

Neste artigo vemos que está claro que o Swagger é uma ótima forma de documentar APIs REST e com a biblioteca SpringFox, caso esteja desenvolvendo uma API Spring Boot, a geração da especificação do Swagger é facilitada. Assim, sempre que possível documente as suas aplicações utilizando esta ferramenta. Tenho certeza que isso irá facilitar o trabalho de todos que consomem a sua API, incluindo você.

Criando uma API REST com o Spring Boot

Até pouco tempo o Java possuia um ciclo atualizações grande, entre o lançamento da versão 5 (07/2004) até a versão 9 (07/2017) foram treze anos. A partir desta nona versão o ciclo foi alterado e agora há lançamento de novas versões duas vezes ao ano, nos meses de março e setembro, por isso que entre a versão 9 e a 12 (a mais atual), o intervalo é de 1 ano e meio.

Este grande intervalo entre as antigas versões era necessário devido ao seu complexo processo de atualização. Visando estabilidade e segurança, cada versão passava por vários testes e interações até serem liberados para os usuários. É preciso dizer que infelizmente nem sempre a versão lançada fornecia estabilidade e segurança. Qualquer usuário que já tenha instalado o Java no seu computador sabe que mensalmente havia alguma atualização de segurança.

Tudo isso dificultava a adição de novos recursos na linguagem, o que permitiu o destaque de alguns frameworks. Dentre vários, o que mais ganhou os holofotes foi o Spring. Criado para aplicações Java EE, hoje o Spring é um projeto que contém várias bibliotecas, de segurança (Spring Security) à big data (Spring XD).

Dentre todos os projetos abaixo da asa do Spring, o que mais se destaca é o Spring Boot.

Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Spring Boot

Todo desenvolvedor Java sabe que as vezes configurar uma aplicação pode ser um trabalho hercúleo. Às vezes são horas de configurações e apenas alguns minutos de codificação.

O Spring Boot veio para resolver esta situação. Utilizando como base o Core do Spring, o Spring Boot trabalha seguindo convenções e configurações padrão para abstrair o máximo possível das configurações necessárias de uma aplicação Spring.

Basta você definir qual tipo de aplicação deseja criar, escolher o starter apropriado, que o Spring se encarregará as configurações básicas necessárias da aplicação que escolheu.

O starter escolhido contém todas as dependências que sua aplicação necessita para funcionar. Então não é necessário nem se preocupar com as dependências do projeto.

Mesmo funcionando através de convenção, caso queira, é possível customizar todas as configurações da aplicação, de forma simples ou complexa, dependendo do que pretende fazer.

O maior benefício deste framework é que permite o desenvolvedor se preocupar com o ponto mais importante da aplicação, as regras de negócio.

Para demonstrar esta facilidade, neste artigo veremos como definir uma API REST utilizando este framework.

Criando a aplicação

Existem algumas formas de criar uma aplicação Spring Boot. Ela pode ser criada no Eclipse, utilizando a IDE Spring Tool Suíte, que também é fornecida como plug-in para o Eclipse, Visual Studio Conde e Atom. Ou mesmo no site Spring Initializr.

No momento estou utilizando uma máquina Mac OS X com o Visual Studio Code instalando, assim vou criar a aplicação inicial utilizando a extensão deste editor fornecida pela Microsoft:

Ao criar, a aplicação terá a estrutura abaixo:

Neste projeto os pontos mais importantes são o arquivo pom.xml, que contém o starter web e das dependências que definimos:

<?xml version="1.0" encoding="UTF-8"?>
<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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>br.com.treinaweb</groupId>
    <artifactId>springbootapi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

E o arquivo DemoApplication.java que contém o nosso método main:

package br.com.treinaweb.springbootapiexemplo;

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

@SpringBootApplication
public class DemoApplication {

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

}

Caso não esteja habituado com o Spring Boot, pode estranhar a criação de um método main em uma aplicação web. Isso ocorre porque este método será responsável por carregar todas dependências embutidas na aplicação, isso inclui o servidor web (o Tomcat).

Desta forma, para esta aplicação web não é necessário definir um arquivo war e adicioná-lo no servidor.

Caso execute a aplicação agora, notará que o servidor embutido será iniciado e a aplicação já poderá ser utilizada. Só que no momento ela não possui nada.

Criando a entidade e repositório

Uma das conversões que o Spring Boot adota é que ele reconhece como componentes da aplicação, todas as classes definidas no mesmo pacote da classe que contém o método main ou em um package “abaixo” do package.

Ou seja, como a nossa classe está definida no pacote br.com.treinaweb.springbootapiexemplo, qualquer classe definida nele ou em um “subpackage” dele, será reconhecida pelo Spring Boot. Desta forma, vamos definir a nossa entidade no pacote br.com.treinaweb.springbootapiexemplo.entity:

package br.com.treinaweb.springbootapi.entity;

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

@Entity
public class Pessoa
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(nullable = false)
    private String nome;

    public long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

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

Já o repositório ficará no pacote br.com.treinaweb.springbootapi.repository:

package br.com.treinaweb.springbootapi.repository;

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

import br.com.treinaweb.springbootapi.entity.Pessoa;

@Repository
public interface PessoaRepository extends JpaRepository<Pessoa, Long> { }

Note que para o repositório foi necessário apenas estender da interface JpaRepository do Spring Data. Esta interface possui métodos para as operações padrão de um CRUD.

Aproveitando que definimos o repositório, vamos configurar o banco de dados. Esta configuração deve ser definida no arquivo application.properties:

## Database Properties
spring.datasource.url = jdbc:mysql://localhost:3306/treinaweb?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update

Alguns comportamentos do Spring Boot podem ser alterados com configurações neste arquivo. Vamos deixar apenas as configurações do banco.

Controller

Por fim, vamos definir o controller da nossa aplicação. Assim como antes, ele será definido em um sub-package do package padrão da aplicação:

package br.com.treinaweb.springbootapi.controller;

import java.util.List;
import java.util.Optional;

import javax.validation.Valid;

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.treinaweb.springbootapi.entity.Pessoa;
import br.com.treinaweb.springbootapi.repository.PessoaRepository;

@RestController
public class PessoaController {
    @Autowired
    private PessoaRepository _pessoaRepository;

    @RequestMapping(value = "/pessoa", method = RequestMethod.GET)
    public List<Pessoa> Get() {
        return _pessoaRepository.findAll();
    }

    @RequestMapping(value = "/pessoa/{id}", method = RequestMethod.GET)
    public ResponseEntity<Pessoa> GetById(@PathVariable(value = "id") long id)
    {
        Optional<Pessoa> pessoa = _pessoaRepository.findById(id);
        if(pessoa.isPresent())
            return new ResponseEntity<Pessoa>(pessoa.get(), HttpStatus.OK);
        else
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    @RequestMapping(value = "/pessoa", method =  RequestMethod.POST)
    public Pessoa Post(@Valid @RequestBody Pessoa pessoa)
    {
        return _pessoaRepository.save(pessoa);
    }

    @RequestMapping(value = "/pessoa/{id}", method =  RequestMethod.PUT)
    public ResponseEntity<Pessoa> Put(@PathVariable(value = "id") long id, @Valid @RequestBody Pessoa newPessoa)
    {
        Optional<Pessoa> oldPessoa = _pessoaRepository.findById(id);
        if(oldPessoa.isPresent()){
            Pessoa pessoa = oldPessoa.get();
            pessoa.setNome(newPessoa.getNome());
            _pessoaRepository.save(pessoa);
            return new ResponseEntity<Pessoa>(pessoa, HttpStatus.OK);
        }
        else
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    @RequestMapping(value = "/pessoa/{id}", method = RequestMethod.DELETE)
    public ResponseEntity<Object> Delete(@PathVariable(value = "id") long id)
    {
        Optional<Pessoa> pessoa = _pessoaRepository.findById(id);
        if(pessoa.isPresent()){
            _pessoaRepository.delete(pessoa.get());
            return new ResponseEntity<>(HttpStatus.OK);
        }
        else
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

Na classe acima, é importante destacar alguns detalhes:

  • A anotação @RestController permite definir um controller com características REST;
  • A anotação @Autowired delega ao Spring Boot a inicialização do objeto;
  • A anotação @RequestMapping permite definir uma rota. Caso não seja informado o método HTTP da rota, ela será definida para todos os métodos.
  • A anotação @PathVariable indica que o valor da variável virá de uma informação da rota;
  • A anotação @RequestBody indica que o valor do objeto virá do corpo da requisição;
  • E a anotação @Valid indica que os dados recebidos devem ser validados.

Pronto, a nossa aplicação pode ser utilizada.

Para testar os endpoints, vamos utilizar o Postman:

Post

Get

Get id

Put

Conclusão

O Spring Boot facilita muito a criação de aplicações web em Java, caso seja um desenvolvedor desta linguagem, é quase uma obrigação conhecê-lo.

Você pode baixar a aplicação deste artigo no meu Github.

O que é um Micro-Framework?

Os Frameworks são ótimas alternativas para nos auxiliar no desenvolvimento das nossas aplicações. Porém, muitas vezes os Frameworks possuem uma quantidade muito grande de recursos que, no fim, podem não ser utilizados em determinados projetos, causando um gasto desnecessário de recursos nos servidores.

Para estes casos, o uso de um Micro-Framework pode ser mais adequado. Mas você sabe o que é um Micro-Framework?

Definição

Os Micro-Frameworks são Frameworks modularizados que possuem uma estrutura inicial muito mais simples quando comparado a um Framework convencional.

Pense em um Micro-Framework como uma peça de LEGO. Inicialmente, um projeto criado com o micro-framework possui apenas o básico para funcionar, (normalmente, sistema de rotas), porém, ao decorrer do projeto, podem haver necessidades para utilização de outros recursos como conexão de banco de dados, sistemas de templates, envio de email, etc. A partir desta necessidade, novas bibliotecas são “encaixadas” no projeto, como uma estrutura de um LEGO.

Exemplos de Micro-Frameworks

Slim

O Slim é um Micro-Framework PHP que permite criar com facilidade aplicações e APIs de pequeno e médio porte, de forma simples e rápida, mantendo a organização e as melhores práticas do mercado.

Slim - Microframework PHP
Curso de Slim - Microframework PHP
CONHEÇA O CURSO

Flask

O Flask é um micro-framework escrito em Python para criação de aplicações web e APIs RESTful. Possui um núcleo simples e entendível que permite que um projeto possua apenas os recursos necessários para sua execução.

Flask - Fundamentos
Curso de Flask - Fundamentos
CONHEÇA O CURSO

Comparativo

Para entendermos de vez a diferença entre Framework e Micro-Framework na prática, possuímos aqui no blog um artigo bem legal a respeito: Django ou Flask, eis a questão.
Desta forma, este comparativo pode te ajudar a esclarecer de vez a diferença entre os dois conceitos e te auxiliar na escolha do melhor para o seu projeto.

O que é uma API?

Você já deve ter ouvido falar, pelo menos alguma vez, sobre esse termo chamado “API” e pode ter pensado o que realmente vem a ser uma API e como ela pode ajudar os desenvolvedores no seu trabalho do dia a dia. Vamos ver então neste artigo o que vem a ser uma API.

Uma API (Application Programming Interface) pode ser definida como um conjunto de padrões que permite a construção de aplicativos, onde ele conecta aplicações, podendo ser utilizada nos mais variados tipos de negócios.

Apesar de ter essa integração, quando um usuário estiver navegando em um site que tem uma integração com uma API por exemplo, nem saberá que sua aplicação está fazendo uma comunicação com uma API, pois ela é invisível ao usuário comum, já que ele enxerga apenas a interface dos softwares e aplicativos.

Com a API você tem uma interface para que um sistema se comunique com outro sistema, compartilhando suas ações e ferramentas. A comunicação é feita através de vários códigos, definindo comportamentos específicos.

Além de conectar sistemas, elas têm um papel muito importante na criação de aplicativos porque nos ajudam muito na produtividade. Você mesmo com certeza já acessou um site onde tinha uma integração com uma API. Vou te explicar melhor com dois exemplos.

Java - Arquivos e I/O API
Curso de Java - Arquivos e I/O API
CONHEÇA O CURSO

Imagine que você está navegando por um site para a compra de um produto. Depois de escolher, você deseja saber quanto isso custará para chegar até o seu endereço e quais são as opções de envio (PAC, SEDEX, etc) e o tempo estimado. Quando você digita o seu CEP para esse cálculo de frete, o site está utilizando provavelmente uma API dos Correios. Isso também vale na hora de efetuar o pagamento, caso o site também aceite PayPal ou o PagSeguro, por exemplo. A transação financeira acaba sendo por meios dessas APIS e diretamente com o site do meio de pagamento. Ou seja, a integração desses sistemas e os sites que utilizamos como exemplo, se dão por meio de uma API.

Outro exemplo é a API do Google Maps. Se você está num site procurando um hotel, já deve ter percebido que aparece um mapa do Google Maps indicando onde é o local exatamente, e através deste mapa você consegue até mesmo navegar por ele.

Muitas pessoas podem confundir uma API com um outro termo muito falado ultimamente, que são os endpoints. Um endpoint é basicamente o que um serviço expõe e esse serviço pode ser acessado por uma aplicação, por isso muitas vezes acaba sendo confundido com uma API, mas vale ressaltar que não é.
Um endpoint contém três principais características: Address (onde o serviço está hospedado), Binding (como o serviço pode ser acessado) e Contract (o que tem no serviço). Além disso, uma API pode existir sem um endpoint e vice-versa.

Python - Banco de dados com DB API
Curso de Python - Banco de dados com DB API
CONHEÇA O CURSO

É seguro utilizar uma API?

As APIs são muito úteis e tem diversos benefícios, além disso proporciona uma troca de informações muito segura, já que somente o proprietário da aplicação define quais informações estarão disponíveis. Você pode e deve utilizar uma API quando necessário, mas sempre visando pontos importantes de segurança.

É importante utilizar SSL nas conexões das APIS, assim toda a comunicação e dados enviados pelas APIS serão transportados criptografados pelo HTTPS. A autenticação também é importante para isolar o que pode ser fornecido de informação para cada um dos softwares que chama a API. Um exemplo são os tokens, que são validados como se fossem uma senha, pois são identificadores únicos que são enviados juntos das chamadas aos endpoints das APIs.

Para levar as informações de um lado para o outro, geralmente é utilizado o JSON, muito utilizado para retornar os dados das APIS baseadas em web. Além disso, esse conceito ocupa pouco espaço e é fácil de transportar via rede.

Apesar de estarmos falando o tempo todo de APIs mais voltadas para web, uma API não necessariamente é utilizada via web. Quem desenvolve softwares desktop pra Windows também pode utilizar APIS, como por exemplo utilizar as APIs do sistema operacional.

Depois que você tiver mais experiência além de consumir APIS de outras pessoas ou empresas, você pode construir as suas próprias, assim você pode utilizar em diversos outros projetos, poupar tempo e até mesmo disponibilizá-las para outras pessoas usarem.

Como melhorar o retorno das suas APIs no Laravel com API Resource do Eloquent

Quando estamos construindo uma API, alguns pontos são extremamente importantes para que o cliente consiga consumir corretamente as informações. Um desses pontos é o modo como retornamos dados da nossa API.

Em aplicações web existe uma preocupação muito grande com a aparência e usabilidade na hora de mostrar as informações. Quando estamos trabalhando com APIs nossa preocupação não deve ser diretamente ligada a esses detalhes, porém precisamos cuidar de outras características para que os clientes da nossa API consigam utilizá-la de maneira correta. Um desses cuidados deve ser a estrutura que as informações são retornadas.

Como os dados são transferidos em uma API

Geralmente as APIs são utilizadas para integração entre sistemas diferentes. Esses sistemas podem apresentar arquiteturas completamente distintas, por esse motivo os dados são transferidos para um formato intermediário conhecido tanto pela API quanto pelo cliente. Os formatos mais utilizados são JSON, XML e YAML.

O processo de transformação dos dados para esse formato intermediário normalmente é chamado de serialização. No Laravel temos recursos bem importantes para serialização, aconselho a leitura do artigo onde falamos o que é e como utilizar serialização JSON no Laravel. O grande detalhe é que mesmo com os recursos específicos que a serialização possui ele não permite personalizar totalmente a resposta, nesse ponto entra o API Resource.

Laravel - Eloquent ORM
Curso de Laravel - Eloquent ORM
CONHEÇA O CURSO

O que é API Resource

O API Resource é uma camada extra que usamos na API para transformar os dados que vamos enviar ao cliente. Ela permite que a estrutura de retorno seja totalmente personalizada, isso nos permite formatar os dados na melhor maneira para entregar ao cliente. Além de garantir o desacoplamento com o model, uma vez que podemos definir a estrutura de forma separada.
O conceito da camada de transformação não é exclusividade do Laravel. Inclusive no próprio PHP temos algumas outras bibliotecas que facilitam esse trabalho, uma das mais conhecidas é o Fractal. O API Resource do Laravel facilita bastante a vida quando estamos dentro do Framework, mas também é possível utilizar qualquer outra biblioteca.

Resource

O primeiro conceito que precisamos conhecer é o resource. Ele é uma classe que usamos para transformar um único item de um recurso. Falando assim parece um pouco complicado, mas na prática o que ele faz é pegar um item que precisamos converter e transformar para o formato especificado nele. Vamos imaginar que estamos buscando o produto Iphone X no banco de dados e precisamos devolver ele na API com uma formatação específica, é basicamente isso que o Resource faz.

A classe do resource deve possuir um método chamado toArray, esse método precisa retornar um array com os dados que deseja que o recurso possua após a conversão. O Laravel injeta automaticamente as propriedades do nosso model no resource, isso nos permite acessá-las através do escopo this. Veja o exemplo abaixo:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'codigo' => $this->id,
            'nome'   => $this->name,
            'preco'  => $this->price,
        ];
    }
}

Basicamente o que estamos fazendo é pegar os dados vindos do model (id, name e price) e colocando respectivamente nos itens do array (código, nome e preço).

O modo como usamos o resource é extremamente simples, basta passarmos 1 item do model no construtor da classe:

new App\Http\Resources\ProductResource(App\Product::find(10));

A instância acima pode ser retornada diretamente em uma action do controller ou em uma rota que utiliza função anônima.

A classe acima parece não fazer muito sentido olhando à primeira vista, porém ganhamos algumas vantagens imprescindíveis em usar essa camada:

  • Liberdade para definição da estrutura
  • Possibilidade de enviar dados extras, como, relações ou gerados através de outros métodos da aplicação
  • Podemos alterar o nome das propriedades do nosso model sem problemas, pois agora definimos o nome que será retornado ao usuário diretamente na estrutura;
  • O nosso model não está preso a somente um modo de exibição, caso necessário podemos criar várias classes de Resource para o mesmo model.
Laravel - Framework PHP (Parte 1/3)
Curso de Laravel - Framework PHP (Parte 1/3)
CONHEÇA O CURSO

Resource Collection

A classe que vimos acima é usada para conversão de apenas um item do model. É comum, porém, precisamos retornar uma coleção de itens na nossa API. Nesse caso utilizamos o Resource Collection. Ele é muito útil para retornar meta informações sobre o conjunto de dados que estamos retornando.
Nele também usamos o método toArray para montar o retorno da nossa aplicação. E usamos a propriedade collection para retornar as informações da coleção:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class ProductCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'data' => $this->collection,
            'links' => [
                'self' => 'link-value',
            ],
        ];
    }
}

O modo de usar é basicamente igual ao Resource, porém precisamos passar um conjunto de informações:

new App\Http\Resources\ProductCollection(App\Product::all());

Os dados buscados no banco serão colocados dentro da chave data do nosso resultado. O Laravel automaticamente utiliza o Resource específico para converter cada item da coleção com base na convenção do nome, com isso terá a estrutura definida tanto para cada item específico da coleção como da estrutura geral de retorno.

Conclusão

O modo como retornamos as informações na nossa API é um dos itens mais importantes. Isso juntamente com o uso correto e semântico HTTP dos verbos na requisição e dos códigos na resposta são os itens onde os desenvolvedores mais pecam e que precisa de atenção no desenvolvimento de APIs. Não deixe também de explorar um pouco a documentação do API Resouce no site do Laravel, ele possui recursos para ajudar com relações, meta informações, personalização da resposta HTTP entre outros.

Já leu nossos artigos sobre o ecossistema Laravel e Eloquent? Veja como usar escopos do Eloquent para construir consultas mais limpas no Laravel, como remover a lógica das views com presenters no Laravel e como aliviar seus controllers com os eventos do Eloquent no Laravel, tudo aqui no blog da Treina Web.

Desenvolvedor Laravel Full-Stack
Formação: Desenvolvedor Laravel Full-Stack
Nesta formação você aprenderá desenvolver aplicações PHP usando o framework Laravel com maestria. Ao final desta formação, você terá condições de trabalhar em grandes aplicações web ou APIs integradas com diversos serviços, tudo isso utilizando as melhores práticas do mercado.
CONHEÇA A FORMAÇÃO

© 2004 - 2019 TreinaWeb Tecnologia LTDA - CNPJ: 06.156.637/0001-58 Av. Paulista, 1765, Conj 71 e 72 - Bela Vista - São Paulo - SP - 01311-200