Aplicação contendo arquitetura modelo para projetos de API's construídas com .Net.
Tecnologias e Bibiotecas:
- Framework : .NET Core 3.1
- [AutoFixture - Tests]
- [Fluent Assertions - Tests]
- [Moq - Tests]
- [NUnit - Tests Framework]
- [Swagger - Documentation]
- [NewtonSoft - JSON]
- [FluentValidation - JSON]
- [Flurl - External Services]
Esse tópico contempla configurações específicas do projeto.
Serão apresentadas o passo a passo das configurações: Swagger, Fluent Validation, Configurações da Aplicação, Injeção de Dependências e Testes Unitários. Vale ressaltar a necessidade de incluir os nugets correspondentes no seu projeto para que seja possível realizar as configurações aqui descritas.
- Swagger: Estrutura de software aberto que auxilia os desenvolvedores na projeção e criação de documentações para Web API's.
Adicionar na classe Startup da sua camanda de apresentação (.API), o seguinte código:
//Define ambiente da aplicação
public IWebHostEnvironment Environment {get;}
//Configuração do Swagger no ConfigureServices
if (!Environment.IsProduction())
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Sample API",
Description = "API para servir de modelo para as demais aplicações",
Version = "v1"
});
});
}
}
//Configuração do Swagger no Configure
if (!env.IsProduction())
{
app.UseSwagger();
app.UseSwaggerUI(swagger =>
{
swagger.SwaggerEndpoint("/swagger/v1/swagger.json", "Sample API V1");
swagger.RoutePrefix = string.Empty;
});
}
}
Feito isso será apresentado o Swagger da aplicação, mapeando os seus DTO's, rotas expostas, status code de resposta e a possibilidade de realizar o teste da sua aplicação.
- Fluent Validation: Biblioteca para criação de regras de validação dos corpos enviados nas requisições.
Nos seus DTO's configure os validators específicos, como no exemplo:
public class ColaboradorValidator: AbstractValidator < ColaboradorDTO > {
public ColaboradorValidator() {
RuleFor(colaborador => colaborador.Nome)
.NotEmpty().WithMessage("O campo de nome deve ser preenchido")
.NotNull().WithMessage("O campo de nome não pode ser nulo");
RuleFor(colaborador => colaborador.Salario)
.NotEmpty().WithMessage("O campo de salario deve ser preenchido")
.NotNull().WithMessage("O campo de salario não pode ser nulo");
RuleFor(colaborador => colaborador.Idade)
.NotEmpty().WithMessage("O campo de idade deve ser preenchido")
.NotNull().WithMessage("O campo de idade não pode ser nulo");
}
}
Após configuradas as regras de validação, acesse a sua classe Statup e acrescente esse trecho de código:
//Configura fluent validation para validação dos corpos da requisições com base no DTO.
services
.AddMvc()
.AddFluentValidation(
fvc => fvc.RegisterValidatorsFromAssemblyContaining<Startup>()
);
Agora, basta acessar o controllador e adicionar o código de verificação da validade do model enviado no corpo da requisição (caso não seja válido recomenda-se o retorno do status code 400 - Bad Request):
if (!ModelState.IsValid) {
return BadRequest();
}
-
Injeção de Dependências: Padrão de projetos utilizado para reduzir o nível de acoplamento entre diferentes módulos e/ou camadas da aplicação. Temos a injeção de dependências implementada nativamente no .NET, assim iremos utilizá-la
-
Configurações da Aplicação
Nessa aplicação, corresponde as variáveis de ambientes que iremos utilizar (como por exemplo: URL's base de API's que serão consumidas na aplicação, tempo de expiração de tokens, dentre outros). Ou seja, tudo que pode ser parametrizado na sua aplicação.
Nesse projeto, acesse a classe InjetorDependencias, inicialmente foi realizado a configuração das variáveis de ambiente da aplicação. Veja:
public static IServiceCollection AdicionarConfiguracoesAplicacao(
this IServiceCollection services,
IConfiguration configuration
) {
return services.Configure<ConfiguracoesAplicacao>(o => {
o.UrlDummy = configuration.GetValue<string>("AppConfiguration:UrlDummy");
});
}
public class ConfiguracoesAplicacao {
public string UrlDummy { get; set;}
}
- Injeção de Dependências
public static IServiceCollection AdicionarComponentesAplicacao(this IServiceCollection services) {
ConfigurarValidator(services);
ConfigurarRepositories(services);
ConfigurarServices(services);
return services;
}
private static void ConfigurarValidator(IServiceCollection services) {
services
.AddTransient<IValidator<ColaboradorDTO>, ColaboradorValidator>();
}
Note que é necessário injetar o validator para a efetividade do Fluent Validation. E, no caso das dependências da aplicação, são injetadas informando qual a interface será consumida pela classe concreta.
Na sua classe Startup, no método de ConfigureServices deverá conter as configurações de injeção de dependências e variáveis da aplicação.
//Configura componentes da aplicação
services.AdicionarComponentesAplicacao();
//Configurações da aplicação
services.AdicionarConfiguracoesAplicacao(Configuration);
- Testes Unitarios: A menor parte testável do seu código.
Os testes unitários no modelo proposto por essa aplicação, contemplam várias bibliotecas, dentre elas: Fixture que é responsável pela criação automática de objetos de entrada e saída dos testes (objects mocks), Fluent Assertions utilizado para definir as regras de asserts dos testes e Moq para criar os mocks das dependências para o teste da classe especificada. O FrameWork de teste escolhido para essa aplicação foi o NUnit.
Classe para testes do controller Colaborador do projeto:
//Define as dependências, como o fixture e a interface repository que é utilizada no controller
private readonly Fixture _fixture = new Fixture();
private Mock<IColaboradorRepository> _colaboradorRepository;
[SetUp]
public void SetupMocks() {
_colaboradorRepository = new Mock<IColaboradorRepository>();
}
/*
Define o teste, note que inicialmente é definido o colaborador DTO e o objeto de retorno do teste - colaborador,
depois é feito um setup para que o retorno seja setado com base na execução do método de adição de colaborador.
O resultado é processado e os casos de assert vem logo abaixo com o resultado esperado.
*/
[Test]
[Description("POST api/colaboradores")]
public async Task DeveRetornar200SeConseguirProcessarAAdicaoDoColaborador() {
var colaborador = _fixture.Create<ColaboradorDTO>();
var colaboradorRetorno = _fixture.Create<Colaborador>();
_colaboradorRepository
.Setup(mock => mock.AdicionarColaborador(colaborador))
.ReturnsAsync(colaboradorRetorno);
var controller = Instanciar();
var resultado = await controller.CadastrarColaborador(colaborador);
resultado
.Should()
.BeOfType<OkObjectResult>()
.Which.StatusCode
.Should()
.Be(200);
resultado
.Should()
.BeOfType<OkObjectResult>()
.Which.Value
.Should()
.BeEquivalentTo(colaboradorRetorno);
}
//Instanciação do controller
private ColaboradorController Instanciar() {
return new ColaboradorController(_colaboradorRepository.Object);
}
- Health Check: Responsável por retornar uma verificação de integridade do status operacional do seu microsserviço.
Inicialmente configure uma classe com a integração que deseja consultar o status de health check, nesse caso, como fazemos integração apenas com a API do Dummy foi a integração escolhida.
//Define a classe de health check
public class DummyAPIHealthCheck: IHealthCheck {
private readonly IColaboradorService _colaboradorService;
public DummyAPIHealthCheck(
IColaboradorService colaboradorService
)
{
_colaboradorService = colaboradorService;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default
)
{
try {
var colaboradores = _colaboradorService.BuscarTodosOsColaboradores();
//Retorna o resultado de status da API com base na obtenção dos colaboradores
if (colaboradores == null) {
return Task.FromResult(
HealthCheckResult.Unhealthy(
description: "API Dummy não retornou os colaboradores"
)
);
}
return Task.FromResult(HealthCheckResult.Healthy());
} catch (Exception ex) {
return Task.FromResult(
HealthCheckResult.Unhealthy(
description: "Falha ao obter todos os colaboradores",
exception: ex
)
);
}
}
}
Para finalizar essa etapa, é necessário preencher no Startup da aplicação o check específico e a rota para obtenção das informações de health check.
//Configure services
//Configura o health check para a aplicação
services
.AddHealthChecks()
.AddCheck<DummyAPIHealthCheck>("Dummy API");
//Configure
//Defina a rota de health
app.UseHealthChecks("/health");