API

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

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

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

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

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

Conhecendo os Health Checks

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

Aplicação que terá a integridade verificada

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

Adicionando o Health Check na aplicação

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

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

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

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

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

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

Integridade do banco de dados

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

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

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

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

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

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

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

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

Alterando as informações exibidas

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

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

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

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

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

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

Criando health checks customizados

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

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

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

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

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

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

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

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

        return builder;
    }
}

E utilizá-lo para registrar o health check:

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

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

Adicionando uma interface

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

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

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

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

    services.AddHealthChecksUI();
}

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

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

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

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

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

    //...
}

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

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

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

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

Por hoje é isso 🙂

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

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

Documentando uma API Spring Boot com o Swagger

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

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

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

Relembrando características do Swagger

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Customizando o Swagger na aplicação Spring Boot

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

Indicando código e mensagem de retorno do Swagger

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    public long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

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

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

Filtrando os endpoints da API Spring Boot no Swagger

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Descrevendo a API no Swagger

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

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

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

O resultado será:

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

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

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

Criando uma API REST com o Spring Boot

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

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

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

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

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

Spring Boot

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

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

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

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

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

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

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

Criando a aplicação

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

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

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

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>br.com.treinaweb</groupId>
    <artifactId>springbootapi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

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

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

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

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

</project>

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

package br.com.treinaweb.springbootapiexemplo;

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

@SpringBootApplication
public class DemoApplication {

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

}

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

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

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

Criando a entidade e repositório

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

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

package br.com.treinaweb.springbootapi.entity;

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

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

    @Column(nullable = false)
    private String nome;

    public long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

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

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

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

package br.com.treinaweb.springbootapi.repository;

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

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

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

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

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

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

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

Controller

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

package br.com.treinaweb.springbootapi.controller;

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

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

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

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

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

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

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

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

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

Na classe acima, é importante destacar alguns detalhes:

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

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

Para testar os endpoints, vamos utilizar o Postman:

Post

Get

Get id

Put

Conclusão

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

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

O que é um Micro-Framework?

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

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

Definição

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

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

Exemplos de Micro-Frameworks

Slim

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

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

Flask

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

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

Comparativo

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

O que é uma API?

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

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

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

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

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

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

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

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

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

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

É seguro utilizar uma API?

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

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

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

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

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

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

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

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

Como os dados são transferidos em uma API

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

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

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

O que é API Resource

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

Resource

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

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

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

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

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

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

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

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

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

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

Resource Collection

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

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

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

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

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

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

Conclusão

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

Conhecendo os recursos de serialização JSON do Laravel

Imagina que compramos uma barraca de acampar e ao chegar em casa montamos. Provavelmente não vamos conseguir carregar a barraca montada, por esse motivo desmontamos, transportamos e depois realizamos a montagem novamente. Quando estamos falando de programação em algumas situações precisamos realizar basicamente o mesmo processo. Em ciência da computação geralmente chamamos de serialização o ato de converter uma estrutura de dados para um formato de transporte ou armazenamento que pode ser reconstruído depois.

Quando trabalhamos com desenvolvimento de APIs é comum precisarmos serializar para o formato no qual eles serão transportados. Na maioria dos casos as APIs utilizam o formato JSON, porém pode ser qualquer outro como XML ou YAML.

Ao falarmos de serialização o Laravel possui algumas características que nos ajuda a realizar a serialização de forma automática e recursos que nos permite personalizar os dados na serialização.

Laravel - Desenvolvimento de APIs REST
Curso de Laravel - Desenvolvimento de APIs REST
CONHEÇA O CURSO

Serialização automática no Controller

Alguns frameworks como Symfony esperam que o retorno de um método do controller seja sempre uma instância da classe response, contudo no caso do Laravel isso é um pouco diferente. Quando retornamos qualquer estrutura iterável em um controller automaticamente ele tenta serializar para JSON e enviar na resposta. Inclusive ele indica no cabeçalho da resposta que estamos retornando application/json.

Faça um teste retornando um array simples ou mesmo o resultado de uma busca com Eloquent:

public function action()
{
    return ["Escola" => "Treinaweb Cursos"];
}

Ao acessar terá:

Mesma coisa também acontece se retornarmos o resultado de uma consulta no banco de dados com Eloquent:

public function action()
{
    return AppProduct::all();
}

Serialização manual

O Laravel também nos permite serializar os dados manualmente. Basta chamarmos o método toJson em uma collection. Como os models do Eloquent sempre retornam uma collection quando buscamos por mais de um registro, podemos trabalhar do seguinte modo:

$products = AppProduct::all();

$json = $products->toJson();

Um detalhe importante! Quando fazemos a serialização o resultado é uma string simples com uma marcação no formato JSON. No caso acima, se retornarmos a variável $json em um controller o content-type será text/html. O correto então é montar a resposta informando manualmente que estamos retornando um application/json.

Escolhendo os dados que não serão serializados

Muitas vezes não podemos retornar todos os dados do nosso model para a resposta. Imagine que o produto tem um campo preço de custo, não é nada legal enviar isso para um revendedor, por exemplo.

Nesses casos podemos declarar um atributo no model informando quais propriedades não devem ser incluídas quando realizarmos a serialização:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Product extends Model
{
    /**
     * Atributos que não serão serializados
     *
     * @var array
     */
    protected $hidden = ['cost'];
}

Se por algum motivo em uma serialização específica quiser usar um dos campos escondidos temos um método específico:

$product->makeVisible('cost')->toJson();

Escolhendo os dados que serão serializados

Ao contrário temos também o atributo que indica apenas os campos que serão usados na serialização:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Product extends Model
{
    /**
     * Atributos que serão serializados
     *
     * @var array
     */
    protected $visible = ['id', 'name', 'price'];
}

Podemos usar um método para esconder as propriedades que estão nessa lista:

return $product->makeHidden('price')->toJson();

Outros recursos

Outros dois recursos podem ajudar muito dependendo da situação. O primeiro nos permite usar um acessor na serialização, basta indicarmos o nome dele no atributo appends do model. Além disso podemos formatar propriedades do tipo data com atributo casts.

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.

REST não é simplesmente retornar JSON: indo além com APIs REST

É até comum, de certa forma, ouvirmos alguém falar que construiu uma API REST porque acabou disponibilizando um endpoint que retorna alguma informação no formato JSON. Mas isso, infelizmente, é um equívoco. Criar uma API REST nada tem a ver com simplesmente retornar algum JSON.

Neste post, vamos discutir sobre os conceitos de REST e JSON e verificar que, apesar de serem conceitos muito íntimos hoje, tecnicamente um não não tem nada a ver com o outro.

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

Mas, afinal de contas, o que é REST?


Hmmm, pelo jeito é algo arquitetural…

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.

O conceito do REST foi criado pelo norte-americano Roy Fielding. Roy é também um dos principais responsáveis pela especificação técnica do protocolo HTTP. Sim, esse mesmo protocolo que você está utilizando nesse exato momento para visualizar esta página em nosso blog. A idéia 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.

Mas quais são as características do protocolo HTTP?

O protocolo HTTP, por decorrência de sua arquitetura, possui algumas características bem marcantes:

  • É um protocolo cliente-servidor: o protocolo HTTP se caracteriza por uma conexão feita entre uma máquina cliente e uma máquina servidora. Quando você acessa o nosso blog, por exemplo, você está fechando uma via de comunicação entre a sua máquina com o nosso servidor. É através dessa conexão que seu browser baixa o HTML, o CSS, o JavaScript e as imagens necessárias para renderizar a página solicitada;

  • A comunicação entre o cliente e o servidor é feita através de “mensagens HTTP”: o tráfego de dados entre um cliente e um servidor é feito através de porções de informação chamadas mensagens. Em uma “rodada” de comunicação, nós temos duas mensagens envolvidas: a mensagem enviada pelo cliente solicitando algo, chamada de request; e a resposta do servidor para a solicitação realizada, chamada de response. Estes dois componentes são essenciais para que a comunicação ocorra seguindo o protocolo HTTP e não é possível ter um request sem que depois haja um response e vice-versa;

  • O protocolo HTTP por definição não é “keep alive”: por padrão, uma conexão HTTP só dura até o momento em que o ciclo request-response é concluído. Logo após este ciclo, a conexão é automaticamente encerrada, ou seja: a conexão morre. Caso algum novo conteúdo precise ser trafegado entre o cliente e o servidor, uma nova conexão é aberta. Hoje, até existe maneiras de se modificar esse comportamento utilizando-se algumas flags na requisição (uma destas flags se chama justamente keep-alive), mas é importante destacar que o protocolo originalmente não foi planejado para essa finalidade;

  • O protocolo HTTP é utilizado de maneira assíncrona na maioria dos clientes: requisições HTTP são assíncronas por definição do lado dos clientes. Isso quer dizer que, se você precisa disparar duas requisições para baixar dois conteúdos distintos, o cliente pode disparar essas requisições ao mesmo tempo, sendo que cada uma delas pode ser respondida em um tempo diferente. O protocolo HTTP foi concebido para funcionar desta forma. Inclusive, quando você renderiza uma página, você pode verificar este comportamento observando a aba “Network” do Web Inspector se você utiliza browsers baseados no WebKit/Blink, como o Chrome:

Na figura acima, temos a inspeção do carregamento da página inicial do nosso blog. Cada um dos componentes listados abaixo do gráfico representa um componente que foi solicitado ao servidor, ou seja: houve um ciclo de requisição HTTP para a obtenção de cada um daqueles componentes, sendo que cada ciclo deste foi transmitido dentro de uma conexão própria ao servidor. O gráfico acima mostra o momento que a requisição foi disparada, bem como quanto tempo o servidor demorou para devolver a response correspondente.

Veja que no gráfico acima, podemos notar que o browser dispara várias requisições ao mesmo tempo, sendo que cada uma tem sua resposta em seu devido tempo. Isso prova que o protocolo HTTP é tratado de maneira assíncrona. Esse processo, inclusive, é chamado de pipelining.

Se você já lidou com AJAX por exemplo, já deve ter ouvido falar sobre o termo callback. Nós precisamos apelar para callbacks (ou promisses ou observables) justamente por causa desta característica do protocolo HTTP: nós precisamos de uma resposta do servidor, mas, pelo cliente fazer a requisição de maneira assíncrona, nós não sabemos exatamente quando essa resposta vai chegar… Por isso criamos callbacks (ou promisses ou observables) para serem executados quando o ciclo request-response for finalmente concluído.

  • As conexões no protocolo HTTP são independentes: como as conexões são abertas e fechadas a medida que algum conteúdo precisa ser trafegado, as conexões acabam sendo independentes umas das outras. Junto à característica assíncrona do protocolo HTTP, isso torna tecnicamente inviável que uma conexão possa “se comunicar” com alguma outra que esteja em curso, ou mesmo conhecer quais outros ciclos request-response estão em curso em determinado momento;

  • O protocolo HTTP é “stateless”: por decorrência dos três últimos pontos, nós afirmamos que o protocolo HTTP é stateless, ou seja, ele não guarda estado das requisições. Mais uma vez, isso é inviável, já que as conexões HTTP são independentes, assíncronas e, principalmente, por não serem keep alive. Se a conexão é imediatamente fechada após sua utilização, como podemos guardar alguma informação sobre ela? É exatamente por essa característica do protocolo HTTP que acabamos utilizando técnicas (como os cookies) para tentar guardar alguma informação necessária, como o usuário que está logado em uma aplicação por exemplo.

  • O protocolo HTTP é semântico: os recursos que podem ser disponibilizados por um servidor HTTP (como um página, por exemplo) podem ser acessados através de URIs (Unique Resource Identifier), que podem ser “traduzidas” para URLs (Unique Resource Locator). O grande ponto é que um servidor Web pode disponibilizar não somente páginas, ele também pode, por exemplo, fazer um upload de um arquivo. Para traduzir o que deve ser feito no servidor, o protocolo HTTP disponibiliza algo que nós chamamos de verbo ou método HTTP. A idéia é que esse verbo, associado ao request, indique o que deve ser feito no servidor.

Nós temos vários verbos HTTP, mas os principais, de maneira sucinta, são:

1) GET: indica que um recurso será recuperado do servidor. Por exemplo, quando você solicita uma página pelo seu browser;

2) POST: indica que um recurso será inserido ou criado no servidor. Um upload de um novo arquivo, por exemplo;

3) PUT: indica que um recurso será atualizado no servidor. Seria equivalente a um update em uma base de dados;

4) DELETE: indica que um recurso será removido do servidor. Seria o equivalente a um delete em uma base de dados.

Isso quer dizer que nós podemos invocar uma mesma URL (ou URI) em uma requisição HTTP, porém, dependendo da atribuição do verbo HTTP, a requisição irá desempenhar uma tarefa diferente no servidor. O verbo HTTP acaba determinando a semântica – ou significado/intenção – da requisição HTTP.

E o JSON? Onde ele entra na jogada?

O JSON (JavaScript Object Notation) não é um protocolo de transporte de informações como o HTTP. Ele é somente uma forma bem leve de representação e troca de informações. Ele tem a única função de levar informações de um lado para o outro. Nós podemos utilizar o JSON para transportar informações entre mensagens HTTP.

JSON x XML


Mas calma! A “treta” não precisa começar, rs

O XML também é uma forma de representação de informações. Porém, é uma forma mais “pesada” e verbosa de representação, já que preza pela “legibilidade” das informações a serem representadas.

Veja, por exemplo, um fragmento de um XML:

<clientes>
    <cliente>
        <id>1</id>
        <nome>TreinaWeb Cursos</nome>
        <idade>10</idade>
    </cliente>
</clientes>

A mesma informação acima poderia ser representada facilmente com o JSON abaixo:

"clientes" : [
    {
        "id" : 1,
        "nome" : "TreinaWeb Cursos",
        "idade" : 10
    }
]

Veja que, apesar de não ser tão explícita, a forma de representação com o JSON é muito mais direta e simples do que através do XML. Perceba também a quantidade de caracteres utilizados em cada uma das representações… Isso é um ponto muito importante! Como o JSON utiliza menos caracteres que o XML, ele também vai ocupar menos bytes dentro de um response com relação ao XML e, por consequência, o download de um response que contenha dados no formato JSON será mais rápido do que um response com as mesmas informações no formato XML. Essa é uma das principais justificativas para os desenvolvedores preferirem utilizar JSON do que XML para o intercâmbio de informações.

Então, o REST e o JSON possuem responsabilidades completamente diferentes!?


Me desculpa se isso foi um balde de água fria… 🙁

Sim, exatamente esse é o ponto! REST é um conceito arquitetural muito complexo, mas que no fim visa tirar vantagem de todas as características do protocolo HTTP, que é um protocolo de transporte. O JSON é somente uma forma de representar informações que precisam ser transportadas de um lado para outro. Sendo assim, podemos utilizar o protocolo HTTP para fazer o transporte de dados entre um cliente e um servidor.

Para conseguir utilizar o protocolo HTTP da forma correta, nós podemos adotar uma arquitetura baseada no REST. Agora, para fazer a representação das informações que precisam ser transportadas através do protocolo HTTP em uma arquitetura REST, nós podemos utilizar o JSON.

Mas então, eu posso ter uma API REST que responde XML?


Isso pode ser interessante…

Absolutamente, sim! Você, neste caso, só estará alterando a forma como as informações serão representadas nas requisições HTTP. Lembre-se: as responsabilidades do HTTP, do REST, do JSON e do XML são completamente diferentes. Isso, inclusive, é um trunfo da arquitetura REST: a representação das informações e o modo de transporte destas são completamente desacopladas.

Inclusive, é muito comum que APIs aceitem dados tanto no formato XML quanto no formato JSON, além de também responderem nestes dois formatos. As linguagens modernas hoje praticamente oferecem suporte nativo ao formato JSON, o que faz com que a adoção deste seja mais popular. Mas muitos sistemas, principalmente os sistemas legados, ainda são fundamentados no formato XML. Por isso é interessante que as APIs respondam nos dois formatos. Inclusive, as APIs definem a forma como vão fazer a leitura das informações com base no formato com que estas estão representadas, bem como definem o formato de dados a ser utilizado para a resposta, em uma etapa chamada content negociation.

“Perceba a petulância do protocolo HTTP!”


Do clássico meme “Percebe, Ivair, a petulância do cavalo!”

E ele é petulante com muita razão, haha. Afinal, toda a web hoje é fundamentada nele. Qualquer transporte de informação que for necessário entre aplicações hoje em dia passará pelo HTTP. Perceba como as características dele justificam muitas coisas que nós sem querer fazemos no automático quando estamos desenvolvendo soluções baseadas na Web.

Eu costumo dizer que entender os princípios básicos do protocolo HTTP é importantíssimo para qualquer desenvolvedor Web hoje em dia. Quando o desenvolvedor sabe como que as coisas “funcionam por baixo dos panos”, ele se preocupa mais com a maneira como ele vai desenvolvedor, além de entender bem melhor o porquê de algumas coisas serem do jeito que são.

Agora, deixa eu aproveitar o momento para fazer um “jabá”, hahaha: sabia que temos aqui no TreinaWeb um curso sobre o protocolo HTTP? Se você se interessar:

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

Você conhece o GraphQL e como ele pode te ajudar?

Olá, Web Developers!

Muitos aqui já devem ter ouvido falar desse tal GraphQL há algum tempo, mas sem saber ainda o que ele é e por que deveríamos cogitá-lo na nossa stack de desenvolvimento. Talvez ele seja uma boa pedida para o seu projeto.

Vamos entendê-lo um pouco mais?

Prototipação Rápida
Curso de Prototipação Rápida
CONHEÇA O CURSO

Como tudo começou

O GraphQL começou no Facebook. Imagine que para exibir uma lista de posts era necessário o acesso a uma API, e dentro de cada post tinha que vir uma lista dos usuários que curtiram. Dentro de cada objeto de usuário tem que vir o nome, foto do perfil, link para o perfil, se o usuário já é seu amigo, etc.

Pelo menos é isso que nós podemos ver quando acessamos o Facebook pelo navegador. Mas quando estamos no aplicativo mobile, não temos todas essas informações do usuário. O que temos é uma lista de usuários que curtiram e o link para seu perfil.

Poderíamos então evitar que o servidor nos envie dados que não vamos precisar, deixando o nosso app mobile mais rápido e economizando dados (bom para quem está no 3G ou 4G).

Mas, sério, não é legal criar uma nova API apenas para enviar uma estrutura levemente diferente, como “mobile/posts”.

É muito comum também que, em sistemas maiores, os dados fiquem em diferentes bases de dados. Podemos, por exemplo, ter dados relacionais no MySQL, guardar certas informações em forma de documentos no MongoDB e ter pequenos dados para consultas rápidas no Redis.

É aqui que começamos a ver que teremos um bom trabalho: juntar dados de bancos diferentes e enviar uma estrutura diferente para cada tipo de aplicação.

O GraphQL entra em cena

GraphQL

Ao invés de ter que ficar criando uma API para cada estrutura diferente de dados e/ou ficar manualmente fazendo consultas para cada banco e depois juntar os dados, que tal simplesmente dizer a “alguém” o que você precisa?

Para fazermos uma consulta no GraphQL, teríamos algo parecido com isso:

query {
   posts
}

Declaramos uma consulta com query e indicamos que queremos um campo com o nome posts.

Também podemos criar consultas que tenham mais níveis, como:

query {
   posts {
      likes
      comments
      shares
   }
}

É possível, ainda, passar parâmetros, como:

query {
   posts (userId: 157321){
      likes
      comments
      shares
   }
}

Dúvidas comuns

Não gosto de ferramentas do Facebook

O GraphQL é mais uma especificação. Então você pode usar outras ferramentas que fazem o mesmo e que não sejam do Facebook.

Uma ferramenta parecida, que visa resolver o mesmo problema, é o Falcor, desenvolvida pela Netflix.

Se eu for trabalhar com GraphQL, terei que usar React?

Não. O GraphQL pode ser usado com qualquer framework, biblioteca ou linguagem de programação.

A ideia do cliente dizer o que quer não me parece segura…

Fique tranquilo. O GraphQL tem uma coisa chamada resolvers. Com eles você pode cuidar da segurança de seus dados, permitindo ou não que determinado usuário acesse-os.

Ainda acho que não preciso disso. Minha aplicação já está muito boa com as APIs existentes.

Se você acredita que não tem problema e que a sua solução hoje já funciona perfeitamente, ótimo.

A ideia do GraphQL é ajudar a solucionar determinado problema. Se você não tem esse problema em sua aplicação, realmente você não precisa gastar tempo implementando uma ferramenta a mais.

Concluindo

O GraphQL é uma linguagem de consulta que facilita o nosso trabalho na hora de fazer requisições, basta que indiquemos os campos que queremos sem que nos preocupemos de onde os dados estão vindo.

Caso queira fazer alguns testes, acesse o GitHub GraphQL.

Tente executar, por exemplo, a seguinte query:

query {
  repository(owner: "graphql", name: "graphql-js"){
    name
    description
  }
}

O que achou do GraphQL? Já teve algum problema que poderia ter sido resolvido com ele? Acha que precisará dele em um novo projeto? Compartilhe com a gente aí nos comentários!

UX/UI - Introdução
Curso de UX/UI - Introdução
CONHEÇA O CURSO