.NET

29 jan, 2019

ASP.NET Core: dicas úteis para o dia a dia de um desenvolvedor – Parte 02

Publicidade

Neste artigo retomo a série de dicas úteis para o dia a dia de Desenvolvedores ASP.NET Core. Caso ainda não tenha acessado o primeiro artigo ou até mesmo queira de revê-lo, acesse o link a seguir:

Definindo a versão do .NET Core utilizada pelo comando dotnet new

Com o .NET Core temos a possibilidade de instalação de diferentes versões de SDKs numa mesma máquina. Há, contudo, uma limitação: que versão o .NET Core CLI (Command-Line Interface) utilizará como default quando solicitarmos a criação de um novo projeto?

O comando dotnet new assumirá como padrão a versão mais recente instalada para o .NET Core. Mas e se precisarmos, então, gerar novos projetos via linha de comando, tomando como base um release anterior?

Felizmente podemos solucionar isso através da criação de um arquivo global.json em um diretório a partir do qual serão gerados novos projetos. Já abordei inclusive isso no seguinte artigo:

Na imagem a seguir é possível constatar que o comando dotnet –version retornou primeiramente a versão 3 do .NET Core (ainda em Preview), ao passo que dentro do diretório C:\ASP.NET Core 2.2\, foi assumido o .NET Core 2.2 graças à existência de um arquivo global.json no mesmo:

Health Checks no ASP.NET Core 2.2

A implementação de uma Action customizada que será acessada para fins de monitoramento costuma ser uma prática comum em aplicações ASP.NET. Geralmente isso envolve checagens executadas com o intuito de determinar se o projeto em questão se encontra dentro de seu status normal de funcionamento.

O ASP.NET Core 2.2 conta agora com um novo mecanismo de Health Check que simplifica este tipo de verificação. A ativação deste recurso envolve as seguintes alterações na classe Startup:

  • A chamada ao método AddHealthChecks em ConfigureServices;
  • A ativação do middleware de Health Check, com a invocação do método UseHealthChecks (informando um caminho para acesso) em Configure.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace APICotacoes
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Adicionado o serviço de Health Check
            services.AddHealthChecks();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

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

            // Ativando o middlweare de Health Check
            app.UseHealthChecks("/status");

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

Um teste acessando o endereço https://localhost:5001/status via Postman trará como retorno o código HTTP 200 (OK), além da mensagem de texto contendo o valor Healthy:

Mais um pouco sobre Health Checks e o ASP.NET Core 2.2: verificando a disponibilidade de um banco de dados

O projeto Xabaril/AspNetCore.Diagnostics.HealthChecks traz uma série de extensões para o ASP.NET Core 2.2, de forma a simplificar a implementação de Health Checks envolvendo verificações de disponibilidade de bancos de dados.

No exemplo descrito nessa seção, foi adicionado o package AspNetCore.HealthChecks.SqlServer ao projeto, com uma alteração acontecendo na classe Startup. O método AddSqlServer foi acionado logo após a chamada a AddHealthChecks, recebendo como parâmetro a Connection String da base a ser testada (BaseCotacoes):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace APICotacoes
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Adicionado o serviço de Health Check
            //services.AddHealthChecks();

            // Adicionando a verificação de disponibilidade
            // do banco de dados
            services.AddHealthChecks()
                .AddSqlServer(Configuration.GetConnectionString("BaseCotacoes"));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

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

            // Ativando o middlweare de Health Check
            app.UseHealthChecks("/status");

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

Um teste com o servidor de bancos de dados indisponível retornará o código de erro HTTP 503 (Service Unavailable), além da mensagem Unhealthy:

Formatando a resposta dos Health Checks no ASP.NET Core 2.2

Ao invocar o método UseHealthChecks no método Configure da classe Startup, temos ainda a opção de informar como parâmetro uma instância do tipo HealthCheckOptions (namespace Microsoft.AspNetCore.Diagnostics.HealthChecks), a qual permitirá formatar o retorno do middleware de Health Check com informações como o status geral da aplicação e a situação de cada checagem previamente configurada:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Mime;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Newtonsoft.Json;

namespace APICotacoes
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Adicionando a verificação de disponibilidade
            // do banco de dados
            services.AddHealthChecks()
                .AddSqlServer(Configuration.GetConnectionString("BaseCotacoes"));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

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

            // Ativando o middlweare de Health Check
            app.UseHealthChecks("/status",
               new HealthCheckOptions()
               {
                   ResponseWriter = async (context, report) =>
                   {
                       var result = JsonConvert.SerializeObject(
                           new
                           {
                               statusApplication = report.Status.ToString(),
                               healthChecks = report.Entries.Select(e => new
                               {
                                   check = e.Key,
                                   status = Enum.GetName(typeof(HealthStatus), e.Value.Status)
                               })
                           });
                       context.Response.ContentType = MediaTypeNames.Application.Json;
                       await context.Response.WriteAsync(result);
                   }
               });

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

A seguir temos o retorno para uma situação normal da aplicação:

E a resposta quando ocorre indisponibilidade do banco de dados:

Implementando Load Balancing com Nginx

O Nginx pode ser uma opção extremamente simples e eficiente para a implementação de mecanismos de Load Balancing em aplicações ASP.NET Core de médio e pequeno porte, sobretudo se utilizado em conjunto com tecnologias como Docker e Docker Compose. No artigo abaixo demonstro tudo isso através de um exemplo prático:

Referências