API REST

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.

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

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.

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.

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.

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

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.

JUNTE-SE A MAIS DE 150.000 PROGRAMADORES