Posts da Tag: API REST - Blog da TreinaWeb

Desenvolvimento

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.


Ferramentas

Tw Dev Server – como ele pode te ajudar a estudar programação?

Olá Web Developers! Vamos ver o que é e como utilizar o Tw Dev Server para te ajudar na hora de estudar programação.

O que é o Tw Dev Server?

Quando estamos trabalhando com desenvolvimento web precisamos de pelo menos um servidor de arquivos estáticos para nossos arquivos HTML, CSS, JavaScript, imagens, etc. Isso porque se abrirmos nosso HTML diretamente no navegador, certas operações não poderão ser feitas pelo JavaScript por questões de segurança.

Além disso, também é comum a necessidade de uma API para fazermos requisições, onde recuperamos ou salvamos dados quando estamos estudando desenvolvimento web ou criando um aplicativo mobile.

Para que essas necessidades básicas fossem resolvidas surgiu o Tw Dev Server. Ele veio com a finalidade de iniciar um servidor estático para nossos arquivos e também servir como um servidor que recebe requisições através de uma API. Com essa API podemos simular um servidor com banco de dados que recebe requisições para recuperar, salvar, modificar e apagar dados (CRUD). E tudo isso sem precisar de nenhuma configuração ou instalação complicada!

Tw Dev Server - tela de desenvolvimento com navegadores sendo atualizados automaticamente

Instalando o Tw Dev Server

O Tw Dev Server pode ser baixado pelo NPM ou como uma extensão do VS Code.

No NPM, para instalar globalmente em sua máquina, execute:

$ npm install -g @treinaweb/tw-dev-server

Assim que você tiver instalado, seu terminal terá o comando tw-dev-server disponível.

Iniciando um servidor de arquivos estáticos

Para iniciar um servidor de arquivos estáticos basta abrir o seu terminal na pasta raiz do seu projeto e executar:

$ tw-dev-server

Com isso nós já teremos nosso servidor rodando.

Tw Dev Server - Terminal

Assim como você pode ver na imagem acima, ele irá mostrar como você pode acessar seus arquivos pelo navegador. Na mesma máquina basta acessar http://localhost:3002. Entretanto, caso queira acessar de outra máquina ou dispositivo que estejam na mesma rede, basta acessar o endereço que é mostrado logo abaixo.

Como se pode notar, ele utiliza por padrão a porta 3002, mas isso pode ser alterado. Por exemplo, caso a gente queira utilizar a porta 4200:

$ tw-dev-server --port=4200

Para encerrar basta pressionar Ctrl + C.

JavaScript Básico
Curso de JavaScript Básico
CONHEÇA O CURSO

Live Reload e Browser Sync

Quando estamos escrevendo nosso HTML, CSS e JavaScript, pode ser meio cansativo ter que ficar atualizando o navegador a todo momento para ver o resultado do nosso trabalho.

Para isso o Tw Dev Server também possui a funcionalidade conhecida como Live Reload. Com essa função ativada, seu navegador vai atualizar automaticamente assim que você alterar algum arquivo HTML ou JavaScript. Caso a alteração seja em um arquivo CSS, o CSS da página é atualizado mesmo sem precisar atualizar o navegador.

Para ativar essa função, basta adicionar --live ao comando.

$ tw-dev-server --live

Além disso, também há a possibilidade de você querer testar sua página em vários navegadores, dispositivos ou tamanhos de janelas diferentes. E para facilitar isso temos a função Browser Sync, que vai sincronizar todos os navegadores que estiverem acessando a sua página.

Com isso você pode acessar a sua página a partir de vários navegadores, até mesmo do seu celular, e tudo o que você fizer em um navegador (como escrever, clicar num botão ou rolar a página) também acontecerá nos demais navegadores! Essa função também atualiza todos os navegadores quando você altera o seu código, não precisando ativar o --live.

Para ativar o Browser Sync basta adicionar --sync ao comando:

$ tw-dev-server --sync

Fazendo Requisições

Uma das principais funcionalidades do Tw Dev Server é disponibilizar uma API para podermos fazer requisições. E isso sem precisar configurar um back end ou um banco de dados. Porém, lembre-se que essa é uma ferramenta para facilitar nos estudos, portanto não deve ser usada para um produto que será lançado, pois não é otimizada para isso.

Podemos fazer operações GET, POST, PUT e DELETE. Para que possamos fazer requisições para a API, basta adicionar /api ao final do endereço que utilizamos para acessar os arquivos estáticos.
Em resumo, se você acessa os arquivos por http://localhost:3002, as requisições devem ser feitas a http://localhost:3002/api.

Logo após o /api nós adicionamos os nomes que quisermos. Isso será para nos organizarmos.

Painel Visual do Tw Dev Server

Para testar as requisições, ver seus dados, modificá-los, etc, você pode usar a ferramenta visual do Tw Dev Server. Basta acessar https://treinaweb.github.io/tw-dev-server/. Uma vez que o seu servidor estiver rodando, a bolinha ao lado do endereço aparecerá verde.

Tw Dev Server - Painel

Criando Dados

Imagine que a gente esteja num projeto de biblioteca e que queremos fazer uma operação POST para salvar um livro. Podemos, por JavaScript, fazer uma requisição com o seguinte código:

    let livro = {
        "nome": "GraphQL: A revolucionária linguagem de consulta e manipulação de dados para APIs",
        "autor": "Akira Hanashiro"
    };
    fetch('http://localhost:3002/api/biblioteca/livros', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(livro)
    })
    .then(response => response.json());
    .then(console.log)

Teremos como resposta um objeto com um campo id para identificar o dado recém cadastrado. Ele é gerado automaticamente pelo Tw Dev Server.

Não apenas por JavaScript, como também podemos fazer essa operação pela nossa ferramenta visual:

Tw Dev Server - Post

Buscando Dados

Para fazermos uma requisição GET e recuperar todos os livros, bastaria fazer:

    fetch('http://localhost:3002/api/biblioteca/livros')
        .then(response => response.json());
        .then(console.log)

E, claro, também podemos fazer isso na nossa ferramenta visual.

Porém, caso você queira que seja retornado apenas um único dado, pode-se passar o id do elemento. Por exemplo: http://localhost:3002/api/biblioteca/livros?id=1591291655803.

Atualizando Dados

Para atualizar um dado basta fazer uma operação PUT, usar a mesma URL e indicar nela o id do dado que você quer atualizar:

    let novoLivro = {...};
    fetch('http://localhost:3002/api/biblioteca/livros?id=1591291655803', {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(novoLivro)
    })
    .then(response => response.json());
    .then(console.log)

Assim que executar essa requisição teremos como resposta o dado atualizado.

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

Apagando Dados

Por fim temos a função de apagar dados. Para isso basta fazer uma requisição DELETE passando o id do dado que se quer apagar.

    fetch('http://localhost:3002/api/biblioteca/livros?id=1591291655803', {
        method: 'DELETE',
    })
    .then(response => response.json());
    .then(console.log)

Caso você queira apagar todos os dados de uma coleção, basta passar all no lugar do id

    fetch('http://localhost:3002/api/biblioteca/livros?id=all', {
        method: 'DELETE',
    })
    .then(response => response.json());
    .then(console.log)

Em conclusão, o Tw Dev Server é uma ótima ferramenta que pode se aliar aos seus estudos em desenvolvimento web e mobile, seja servindo arquivos ou simulando um back end com api e banco de dados. Também pode ser usado para fazer testes caso você esteja desenvolvendo um front mas o back end ainda não tem uma api pronta para te retornar dados.


Java

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"

React Native - Autenticação com Firebase Authentication
Curso de React Native - Autenticação com Firebase Authentication
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.


Desenvolvimento Back-end PHP

O que é o Slim Framework?

Neste artigo falaremos sobre o Slim Framework, micro-framework escrito em PHP para, principalmente, criação de APIs RESTful.

Open source, multiplataforma, lançado no final de 2010, o Slim ganhou destaque para desenvolvedores que precisam de um Framework para criar serviços REST.

Caso você não saiba o que é um Micro-Framework…

Em um artigo anterior, expliquei “o que é um Micro-Framework”.

Um Micro-Framework 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 lego.

Basicamente, como podemos ver na imagem abaixo:

Representação em lego de uma aplicação e seus complementos

E APIs RESTful?

Aqui no blog também possuímos um artigo sobre os conceitos que cercam as APIs RESTful.

Basicamente, REST é um acrônimo para REpresentational State Transfer, ou seja, Transferência de Representação de Estado. O REST é, no final das contas, um estilo arquitetural que podemos utilizar ou não em nossas aplicações.

A ideia do REST é utilizar de maneira mais eficiente e em sua plenitude as características do protocolo HTTP, principalmente no que diz respeito à semântica do protocolo. O resultado disso ao final das contas é, além da utilização mais “correta” do protocolo, um trânsito de informações mais eficiente e, por consequência, mais rápido.

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

De volta ao Slim…

É um Micro-Framework bastante leve e prático, e como dito anteriormente, possui como principal característica a implementação RESTful, facilitando a vida do desenvolvedor na criação de APIs de pequeno ou médio porte de maneira organizada.

Com o Slim é permitido criar aplicações seguras e robustas de maneira mais simples e com uma baixa complexidade se comparado a demais Frameworks do mercado.

Como principais características, podemos citar:

  • Baixa necessidade de configuração;
  • Por não possuir um padrão de desenvolvimento definido, há liberdade para definir uma estrutura personalizada para o projeto;
  • Facilidade na integração de aplicações externas ao projeto;
  • Possui suporte nativo a rotas HTTP;
  • Possui suporte a injeção de dependências;
  • Possui uma comunidade ativa, entre outros.

Seu download pode ser feito em seu site, que possui toda sua documentação e suporte. Desta forma, é uma excelente opção de tecnologia na criação de serviços REST.

Desenvolvedor PHP
Formação: Desenvolvedor PHP
Nesta formação você aprenderá todos os conceitos da linguagem PHP, uma das mais utilizadas no mercado. Desde de conceitos de base, até características mais avançadas, como orientação a objetos, práticas de mercado, integração com banco de dados. Ao final, você terá conhecimento para desenvolver aplicações PHP usando as práticas mais modernas do mercado.
CONHEÇA A FORMAÇÃO

.NET Core C# Desenvolvimento Back-end

Serviços gRPC no ASP.NET Core

No âmbito de aplicações, quando estamos falando de serviços, o desenvolvedor imagina uma API RESTful. Não dá para negar que o casamento de HTTP+JSON faz um grande sucesso, mas em alguns momentos esta combinação possui limitações.

Em larga escala, ou quando se lida com aplicações críticas, a performance de uma API REST pode significar seu gargalo, principalmente quando estamos lidando com microserviços. É por causa disso que grandes empresas, como Netflix, Digital Ocean, SoundClound e Google, optaram pelo gRPC para melhorar e otimizar a comunicação dos seus microserviços.

O gRPC é um framework RPC open source criado pelo Google, como forma de melhorar a comunicação do grande número de microserviços que possuem. Com suporte à várias linguagens, na versão 3.0 do .NET Core 3.0, a Microsoft adicionou um template que facilita a criação de serviços gRPC.

Antes de conhecer este template e criar um serviço, você precisa conhecer mais sobre o gRPC.

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

O que seria o gRPC?

Como dito, o gRPC é um framework RPC. O RPC é a sigla de Remote Procedure Call, chamada de procedimento remoto, que basicamente trata-se de uma tecnologia para comunicação de processos. Permitindo que um computador possa invocar um procedimento de outro computador, independente da linguagem ou plataforma.

No manifesto de motivação e princípios do gRPC o Google conta que já utilizava uma infraestrutura RPC denominada Stubby. Mas por ser muito associada a infraestrutura da empresa e não ser baseado em nenhum padrão, ela não poderia ser aberta a todos. Com o advento do HTTP/2, o Stubby foi refeito e nasceu o gRPC, que hoje é parte da CNCF, Cloud Native Computer Foundation.

Porque o HTTP/2 é importante?

O gRPC não trabalha sob o protocolo HTTP 1.1, que é o protocolo utilizado por todos os servidores web, devido suas limitações. A principal é a forma que as solicitações são tratadas.

Por mais que não seja aparente, no HTTP 1.1 um servidor só pode receber e responder uma requisição por vez. Para tentar contornar este limite, ele trabalha com paralelismo, mas o canal de comunicação trabalha em apenas uma via. Quando está recebendo não pode enviar e vice-versa.

Já no HTTP/2 as conexões são “multiplexadas” (multiplex), o que significa que trabalha de forma bidirecional, recebendo e enviando várias solicitações ao mesmo tempo.

As duas imagens abaixo ilustram este cenário:

Aqui é importante ressaltar que devido ao uso do HTTP/2, um serviço do gRPC só pode ser hospedado em um servidor que fornece suporte a esta versão do protocolo. Felizmente na versão 3.0 do .NET Core, o Kestrel passou a suportar esta versão.

Para conhecer as características deste tipo de serviço, vamos colocar a mão na massa.

Criando um serviço gRPC no ASP.NET Core

A partir da versão 3.0 do .NET core é possível criar um serviço gRPC através do comando abaixo:

dotnet new grpc -o ServicoGrpc

Ou no Visual Studio 2019, selecione o template gRPC Service.

A aplicação criada terá a estrutura abaixo:

Vamos ver em detalhes seus arquivos.

Contrato com Protobuf

Dentro da pasta Protos temos o arquivo greet.proto:

syntax = "proto3";

option csharp_namespace = "ServicoGrpc";

package Greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

Este é o arquivo de contrato. Baseado no padrão Protobuf, ele que definirá a estrutura do serviço gRPC e precisa existir no cliente e no servidor. É a partir dele que o gRPC irá gerar as operações do serviço.

Acima é definido dentro do pacote Greet um serviço Greeter, que contém uma operação, SayHello. Em seguida é definida a estrutura dos dados recebidos na solicitação e os dados retornados.

Nos bastidores, a biblioteca do gRPC, Grpc.AspNetCore, irá criar classes, que nos permitirá definir o serviço no C#.

Serviços

Os serviços são definidos dentro da pasta Services, que no momento possui apenas a classe GreeterService:

public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

Esta classe herda Greeter.GreeterBase, que foi gerada pelo gRPC com base no arquivo .proto. O mesmo ocorreu com as classes HelloRequest e HelloReply.

Servidor

Por fim, no arquivo appsettings.json é definido que o Kestrel utilizará o protocolo HTTP/2:

"Kestrel": {
  "EndpointDefaults": {
    "Protocols": "Http2"
  }
}

E na classe Startup, além de registrar o serviço gRPC, cria-se um fallback para caso de o usuário acessar o serviço pelo navegador ou utilizando um client sem suporte ao gRPC:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<GreeterService>();

            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
            });
        });
    }
}

Cliente

Para acessar um serviço gRPC necessitamos de um cliente com suporte a este tipo de serviço, para isso, irei utilizar uma aplicação console simples:

class Program
{
    static async Task Main(string[] args)
    {
        var channel = GrpcChannel.ForAddress("https://localhost:5001");
        var client =  new Greeter.GreeterClient(channel);
        var reply = await client.SayHelloAsync(
                        new HelloRequest { Name = "Treinaweb Blog" });
        Console.WriteLine("Saudacao: " + reply.Message);
        Console.WriteLine("Pressione qualquer coisa para sair...");
        Console.ReadKey();
    }
}

Que precisa referenciar as bibliotecas Google.Protobuf, Grpc.Net.Client e Grpc.Tools:

<ItemGroup>
  <PackageReference Include="Google.Protobuf" Version="3.11.1" />
  <PackageReference Include="Grpc.Net.Client" Version="2.25.0" />
  <PackageReference Include="Grpc.Tools" Version="2.25.0">
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemGroup>

Além claro do arquivo greet.proto definido do servidor:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

No servidor, o atributo GrpcServices é definido com o valor Server.

Ao executar ambas as aplicações, inicialmente o serviço e por fim o cliente, poderemos ver a comunicação sendo realizada:

C# (C Sharp) - Introdução ao ASP.NET Core
Curso de C# (C Sharp) - Introdução ao ASP.NET Core
CONHEÇA O CURSO

Conclusão

No momento há poucos servidores web com suporte ao gRPC, mas esta tecnologia possui muito potencial. Em uma arquitetura de microserviços é altamente recomendado que ela seja utilizada para a comunicação entre os microserviços. Neste ambiente a comunicação externa pode se manter com a tradicional combinação HTTP+JSON.

Até a próxima.


Java

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


Desenvolvimento Back-end Java

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.


.NET Core C# Web Services

Criando uma API RESTful com o NancyFx e .NET Core

Quando pensamos em API RESTful no C#, a primeira opção que vem a mente é o ASP.NET Web API. Ele é um ótimo framework, utilizado em vários sistemas, com uma abundante documentação e material de apoio. Mas ele não é a nossa única opção para a criação de API Rest.

Em artigos passados, conhecemos o ServiceStack, mas outra opção que está crescendo é o NancyFX.

Inspirado no framework Sinatra do Ruby, o Nancy é um framework de criação de serviços HTTP, leve e direto ao ponto. Tem por objetivo não atrapalhar o desenvolvedor. Ele obtém isso através de uma série de configurações padrões e convenções. Assim, com o Nancy é possível criar um site em minutos.

Claro que todas as suas convenções e configurações podem ser alteradas, caso este seja o desejo do desenvolvedor.

Para este artigo, mostrarei como é possível criar uma API simples, rapidamente utilizando o Nancy.

Criando a aplicação

Neste artigo estou utilizando o .NET Core, desta forma, mostrarei como criar a aplicação através do terminal. Inicialmente é necessário criar uma aplicação web vazia:

dotnet new web -n NancyAPI

No projeto criado, adicione a referência do Nancy:

dotnet add package Nancy --version 2.0.0-clinteastwood

Agora é necessário dizer ao ASP.NET que iremos utilizar o Nancy, para isso, faremos uso do OWIN.

O OWIN significa “Open Web Interface for .Net. Ele é um conjunto de padrões voltados para o .NET, que visa facilitar e encorajar a implementação de projetos que tentam desacoplar a aplicação do servidor. Na prática ele define um middleware ao pipeline de execução de uma aplicação ASP.NET.

Por se tratar de um middleware, deve ser configurado no método Configure da classe Startup:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseOwin(o => o.UseNancy());
}

Com isso, o Nancy já está ativo na nossa aplicação.

Módulos

No Nancy os endpoints devem ser definidos em módulos. Um módulo nada mais é que uma classe que herde a classe NancyModule. Esta classe pode ser definida em qualquer ponto do projeto, pois o Nancy fará uma busca pelos módulos quando um endpoint for invocado. O único ponto importante é que esta classe seja definida como pública (public) para que possa ser localizada.

Por exemplo:

using Nancy;

namespace NancyAPI.Module
{
    public class HomeModule : NancyModule
    {
        public HomeModule()
        {
            Get("/", _ => "Hello World from Nancy!");
        }
    }
}

Note que a rota está sendo definida como o primeiro parâmetro do método Get. Como é possível supor, esta rota será invocada quando for solicitada através de uma requisição GET.

O Nancy define métodos para os principais verbos do HTTP:

  • Delete = DELETE;
  • Get = GET;
  • Head = HEAD;
  • Options = OPTIONS;
  • Post = POST;
  • Put = PUT;
  • Patch = PATCH.

No segundo parâmetro do método deve ser passado a referência de uma função ( uma Function), que no exemplo acima, se utiliza lambda para definir a função. Este é o padrão recomendado pelo Nancy.

Ao executar a aplicação e acessar o endpoint definido, teremos o resultado:

Simples, não é?

Definindo a API RESTful

Para exemplificar uma API REST, vamos definir um modelo:

namespace NancyAPI.Models
{
    public class Pessoa
    {
        public int Id { get; set; }
        public string Nome { get; set; }
    }
}

E um repositório:

using System.Collections.Generic;
using NancyAPI.Models;
using System.Linq;

namespace NancyAPI.Repositories
{
    public class PessoaRepository
    {
        private static Dictionary<int, Pessoa> pessoas = new Dictionary<int, Pessoa>();

        public List<Pessoa> GetAll(){
            return pessoas.Values.ToList();
        }

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

        public void Add(Pessoa pessoa){
            pessoas.Add(pessoa.Id, pessoa);
        }

        public void Edit(Pessoa pessoa){
            pessoas.Remove(pessoa.Id);
            pessoas.Add(pessoa.Id, pessoa);
        }

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

Agora só falta o nosso endpoint:

using Nancy;
using Nancy.ModelBinding;
using NancyAPI.Models;
using NancyAPI.Repositories;

namespace NancyAPI.Module
{
    public class PessoaModule: NancyModule
    {
        public readonly PessoaRepository repository;
        public PessoaModule()
        {
            repository = new PessoaRepository();

            Get("/pessoa/", _ => repository.GetAll());
            Get("/pessoa/{id}", args => repository.Get(args.id));
            Post("/pessoa/", args => {
                var pessoa = this.Bind<Pessoa>();

                repository.Add(pessoa);

                return pessoa;
            });
            Put("/pessoa/{id}", args => {
                var pessoa = this.Bind<Pessoa>();

                pessoa.Id = args.id;

                repository.Edit(pessoa);

                return pessoa;
            });
        }
    }
}

Neste endpoint é possível notar que os argumentos da URL são obtidos através do parâmetro do lambda:

Get("/pessoa/{id}", args => repository.Get(args.id));

Este parâmetro é um objeto dinâmico. Assim, o Nancy tentará obter o valor do argumento com base na propriedade informada.

Já os dados passados na solicitação, podem ser obtidos pelo Bind:

var pessoa = this.Bind<Pessoa>();

O Nancy irá converter automaticamente os dados obtidos para o tipo de objeto indicado.

Para testar os endpoints, podemos utilizar o Postman:

POST

GET

PUT

GET ID

Conclusão

Graças a sua estrutura simples, porém sofisticada, o Nancy tem atraído cada vez mais a atenção dos desenvolvedores. Se tornando uma ótima alternativa ao ASP.NET Web API.

Caso necessite criar uma API REST, não esqueça de dar uma boa olhada nele.

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

Você pode ver o projeto apresentado neste artigo no meu GitHub.


.NET Core ASP .NET C# Desenvolvimento Desenvolvimento Back-end Web Services

Documentando uma ASP.NET Core Web API com o Swagger

Com cada vez mais aplicações multiplataformas, é comum definir uma API REST que será consumida pelas várias versões da aplicação. Mas com vários times tendo acesso a API, não basta mais apenas desenvolvê-la e esperar que todos consigam utilizá-la institivamente.

Na época do web service, existia o WSDL que funcionava como uma documentação, já que facilitava a criação dos clientes que iria consumí-lo. Com as APIs REST não temos esta facilidade, assim, torna-se imprescindível que ela seja bem documentada.

Mas como realizar esta documentação? Existem algumas ferramentas que podem nos auxiliar neste processo, como: API Blueprint, RAML, Swagger, entre outras.

Como o nome deste artigo sugere, aqui abordaremos o Swagger.

Swagger

O Swagger é uma aplicação open source que auxilia os desenvolvedores a definir, criar, documentar e consumir APIs REST. Sendo uma das ferramentas mais utilizadas para esta função, a empresa por trás dela (a SmartBear Software), decidiu criar o Open API Iniciative e renomearam as especificações do Swagger para OpenAPI Specification.

Ela visa padronizar as APIs REST, desta forma descreve os recursos que uma API deve possuir, como endpoints, dados recebidos, dados retornados, códigos HTTP, métodos de autenticação, entre outros.

Para facilitar o processo de especificação/documentação de uma API, o Swagger fornece algumas ferramentas, como:

  • Swagger Editor: Editor que permite especificar uma API. Há uma versão online grátis e aplicativos para desktop;
  • Swagger UI: Interface interativa para a documentação da API;
  • Swagger Codegen: Gera templates de código (mais de 20 linguagens estão disponíveis) de acordo com uma especificação de API.

Neste artigo vamos focar na documentação de uma API existente, criada em ASP.NET Core Web API.

Especificação do Swagger

O ponto mais importante do Swagger é a sua especificação, que era chamada de Swagger specification e agora é OpenAPI Specification. Esta especificação trata-se de um documento, JSON ou YAML, que define a estrutura da API. Indicando os endpoints, formatos de entrada, de saída, exemplos de requisições, forma de autenticação, etc.

Abaixo você pode ver um exemplo desta especificação definida em JSON:

{
   "swagger": "2.0",
   "info": {
       "version": "v1",
       "title": "API V1"
   },
   "basePath": "/",
   "paths": {
       "/api/Example": {
           "get": {
               "tags": [
                   "Example"
               ],
               "operationId": "ApiExampleGet",
               "consumes": [],
               "produces": [
                   "text/plain",
                   "application/json",
                   "text/json"
               ],
               "responses": {
                   "200": {
                       "description": "Success",
                       "schema": {
                           "type": "array",
                           "items": {
                               "$ref": "#/definitions/Item"
                           }
                       }
                   }
                }
           },
           "post": {
               ...
           }
       },
       "/api/Example/{id}": {
           "get": {
               ...
           },
           "put": {
               ...
           },
           "delete": {
               ...
   },
   "definitions": {
       "Item": {
           "type": "object",
            "properties": {
                "id": {
                    "format": "int64",
                    "type": "integer"
                },
                "name": {
                    "type": "string"
                },
                "isValid": {
                    "default": false,
                    "type": "boolean"
                }
            }
       }
   },
   "securityDefinitions": {}
}

É possível definir esta especificação na mão, olhando as opções na documentação, através do Swagger Editor ou utilizando outra ferramenta para gerá-la.

No ASP.NET Core podemos utilizar duas bibliotecas para gerar este documento:

Por ser mais completa, faremos uso da Swashbuckle.

Adicionando o Swashbuckle na aplicação

Para este exemplo, estou utilizando uma aplicação ASP.NET Core Web API, baseada no exemplo fornecido pela documentação da Microsoft, que pode ser vista aqui. Você pode ver a aplicação que criei no meu Github.

Com a aplicação Web API criada, inicialmente é necessário adicionar o pacote do Swashbuckle na aplicação. Ele possui três componentes:

Mas é necessário instalar apenas o pacote Swashbuckle.AspNetCore. Por se tratar de uma aplicação .NET Core, este pacote pode ser adicionado com o comando abaixo:

dotnet add package Swashbuckle.AspNetCore

Após isso, para que o arquivo de especificação da API do Swagger seja criado, é necessário adicionar o gerador dele nos serviços da aplicação, no método ConfigureServices da classe Startup:

public void ConfigureServices(IServiceCollection services)
{
    //... código omitido

    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new Info { Title = "TodoAPI", Version = "v1" });
    });
}

Para a classe Info, defina o using do namespace abaixo:

using Swashbuckle.AspNetCore.Swagger;

Agora, no método Configure é necessário ativar o Swagger e indicar o local onde o seu arquivo de especificação será criado:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //Ativa o Swagger
    app.UseSwagger();

    // Ativa o Swagger UI
    app.UseSwaggerUI(opt =>
    {
        opt.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoAPI V1");
    });

    //... código omitido
}

Com isso, através de reflection a biblioteca consegue especificar os endpoints da aplicação.

Caso seja executada, podemos acessar o Swagger UI da aplicação no caminho /swagger:

Caso queira que o Swagger UI seja acessado a partir da raiz da aplicação, na ativação dela, no método Configure, atribua vazio para a propriedade RoutePrefix:

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoAPI V1");
    c.RoutePrefix = string.Empty;
});

Customizando o Swagger

Como o Swashbuckle trabalha com reflection, podemos fazê-lo gerar mais informações das nossas APIs com base em configurações definidas nela.

Comentários de documentação

Sabemos que no C# podemos adicionar comentários de documentação nos nossos códigos, como:

/// <summary>
/// Lista os itens da To-do list.
/// </summary>
/// <returns>Os itens da To-do list</returns>
/// <response code="200">Returna os itens da To-do list cadastrados</response>
[HttpGet]
public ActionResult<List<Item>> Get()
{
    return _repository.GetAll();
}

É possível fazer o Swashbuckle ler esses comentários e assim tornar a especificação da API mais detalhada.

Para isso, na definição de geração do arquivo de especificação, no método ConfigureServices deve ser adicionado a informação abaixo:

services.AddSwaggerGen(opt =>
{
    opt.SwaggerDoc("v1", new Info { Title = "TodoAPI", Version = "v1" });

    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    opt.IncludeXmlComments(xmlPath);
});

Note que estamos indicando que ele deve ler os comentários do código a partir de um arquivo XML. Para que eles sejam gerados neste arquivo indicado, no arquivo de configuração do projeto (o .csproj), adicione o trecho abaixo:

<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

O <NoWarn> é definido para que a IDE não exiba um alerta sobre os códigos que não possuírem comentários de documentação.

Agora ao acessar o Swagger API, o que foi definido em <summary> será mostrado:

Também podemos o <remarks> para indicar um exemplo de requisição:

// POST api/todo
/// <summary>
/// Cria um item na To-do list.
/// </summary>
/// <remarks>
/// Exemplo:
///
///     POST /Todo
///     {
///        "id": 1,
///        "name": "Item1",
///        "iscomplete": true
///     }
///
/// </remarks>
/// <param name="value"></param>
/// <returns>Um novo item criado</returns>
/// <response code="201">Retorna o novo item criado</response>
/// <response code="400">Se o item não for criado</response>        
[HttpPost]
public ActionResult<Item> Post([FromBody] Item value)
{
    Console.WriteLine(value?.Name);

    var item = _repository.Save(value);
    if(item != null)
        return item;

    return BadRequest();
}

O resultado será:

Com as anotações ProducesResponseType e Produces podemos indicar os códigos HTTP e o tipo de dado que os endpoints irão retornar:

[Authorize]
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class TodoController : ControllerBase
{
    //..código omitido

    // GET api/todo
    /// <summary>
    /// Lista os itens da To-do list.
    /// </summary>
    /// <returns>Os itens da To-do list</returns>
    /// <response code="200">Returna os itens da To-do list cadastrados</response>
    [HttpGet]
    [ProducesResponseType(200)]
    public ActionResult<List<Item>> Get()
    {
        return _repository.GetAll();
    }

    //... código omitido
}

Especificando a autenticação

Uma das principais vantagens de adicionar o Swagger na aplicação, é poder testar os endpoints pela Swagger UI. Quando a aplicação define autenticação, é necessário indicar isso para o Swashbuckle, para que ele também gere uma especificação desde detalhe.

Com isso, será possível testar os endpoints pela Swagger UI utilizando a autenticação definida.

Para fazer isso, devemos adicionar a informação abaixo, na definição de geração do arquivo de especificação, no método ConfigureServices:

services.AddSwaggerGen(opt =>
{
    opt.SwaggerDoc("v1", new Info { Title = "TodoAPI", Version = "v1" });

    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    opt.IncludeXmlComments(xmlPath);

    var security = new Dictionary<string, IEnumerable<string>>
    {
        {"Bearer", new string[] { }},
    };

    opt.AddSecurityDefinition(
        "Bearer",
        new ApiKeyScheme
        {
            In = "header",
            Description = "Copie 'Bearer ' + token'",
            Name = "Authorization",
            Type = "apiKey"
        });

    opt.AddSecurityRequirement(security);
});

Acima estamos definindo que a aplicação faz uso de autenticação do tipo JWT Token (“Bearer Token”). Na documentação do Swagger, você pode ver os outros tipos de autenticação suportados.

Ao acessar o Swagger UI, ele irá mostrar o botão “Autorize”:

Exibe um botão com o texto "Autorize"

Ao clicar nele, é possível informar o tipo de autenticação especificado:

Com isso, mesmo que necessite de autenticação, será possível realizar as requisições dos endpoints da aplicação pelo Swagger UI.

Descrição

Por fim, é possível descrever um pouco da aplicação através das propriedades da classe Info da especificação do Swagger:

services.AddSwaggerGen(opt =>
{
    opt.SwaggerDoc("v1", new Info
    {
        Version = "v1",
        Title = "Todo API",
        Description = "Um exemplo de aplicação ASP.NET Core Web API",
        TermsOfService = "Não aplicável",
        Contact = new Contact
        {
            Name = "Wladimilson",
            Email = "contato@treinaweb.com.br",
            Url = "https://treinaweb.com.br"
        },
        License = new License
        {
            Name = "CC BY",
            Url = "https://creativecommons.org/licenses/by/4.0"
        }
    });

    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    opt.IncludeXmlComments(xmlPath);

    var security = new Dictionary<string, IEnumerable<string>>
    {
        {"Bearer", new string[] { }},
    };

    opt.AddSecurityDefinition(
        "Bearer",
        new ApiKeyScheme
        {
            In = "header",
            Description = "Copie 'Bearer ' + token'",
            Name = "Authorization",
            Type = "apiKey"
        });

    opt.AddSecurityRequirement(security);
});

O resultado será:

Swagger UI mostrando a descrição da API

Conclusão

O Swagger é uma ótima forma de documentar APIs REST e com a biblioteca Swashbuckle, podemos gerar o arquivo de especificação do Swagger com facilidade. Assim, não há mais desculpa para se definir documentações interativas e detalhadas para as suas APIs REST.