ASP.NET
Core Web API - Clean Architecture - III
![]() |
Hoje vamos criar um projeto ASP.NET Core Web API aplicando o padrão de arquitetura Clean Architecture. |
Continuando o artigo anterior vamos implementar a camada de apresentação no projeto WebAPI da pasta Presentation.
Camada de Apresentação
A camada de
apresentação é um componente da Clean Architecture responsável por enviar
respostas e receber solicitações do usuário. É a camada mais externa de um
aplicativo e é a camada que interage diretamente com o usuário final. É nesta
camada
onde residem os componentes responsáveis pela interação com o usuário, como
interfaces gráficas, APIs, controladores web, etc. Essa camada deve ser
minimalista e apenas traduzir as ações do usuário para chamadas nos casos de
uso.
A camada de apresentação pode ser implementada usando várias tecnologias, como
Web API, gRPC, Web Application, Blazor, React, Vue, etc .
A camada de apresentação não deve conter lógica de negócios ou conhecimento de
domínio e deve apenas interagir com o restante do aplicativo por meio da camada
de aplicativo. O objetivo dessa camada é apresentar a funcionalidade do
aplicativo ao usuário e receber entrada do usuário sem estar vinculado a nenhum
detalhe ou tecnologia de implementação específica.
A camada de apresentação contém o projeto WebAPI
que fornece uma maneira de criar e expor serviços da Web RESTful. O projeto
WebAPI deve fazer referência ao projeto
Application e ao projeto
Persistence.
Vamos iniciar apresentando o controlador UsersController da pasta Controllers:
using
CleanArchitecture.Application.UseCases.Features.CreateUser; using CleanArchitecture.Application.UseCases.Features.GetAllUser; using MediatR; using Microsoft.AspNetCore.Mvc; namespace CleanArchitecture.WebAPI.Controllers;[Route( "api/[controller]")][ApiController] public class UsersController : ControllerBase { private readonly IMediator _mediator; public UsersController(IMediator mediator) { _mediator = mediator; } [HttpGet] public async Task<ActionResult<List<GetAllUserResponse>>> GetAll(CancellationToken cancellationToken) { var response = await _mediator.Send(new GetAllUserRequest(), cancellationToken); return Ok(response); } [HttpPost] CancellationToken cancellationToken) { var response = await _mediator.Send(request, cancellationToken); return Ok(response); } } |
Neste controlador temos os atributos [Route] e [ApiController] que são usados para definir a rota base da API para esse controlador. [controller] é uma convenção que será substituída pelo nome do controlador, no caso, "UsersController". O atributo [ApiController] indica que o controlador deve ter comportamentos específicos para APIs, como validação automática de modelo e outras características.
A classe UsersController herda da classe ControllerBase, que é a classe base para controladores em ASP.NET Core. Ela fornece funcionalidades relacionadas à criação de APIs.
O construtor do controlador recebe uma instância de IMediator. Isso indica que a classe está usando o padrão Mediator para lidar com as operações de solicitação e resposta. O Mediator é uma parte do padrão CQRS (Command Query Responsibility Segregation) e é usado para separar as operações de leitura e escrita.
O método GetAll é um endpoint HTTP GET que recupera todos os usuários. Ele chama o Mediator para enviar uma solicitação GetAllUserRequest. Quando a resposta é recebida, ela é retornada como um resultado HTTP 200 OK.
O método Create é um endpoint HTTP POST para criar um novo usuário. Ele recebe uma solicitação CreateUserRequest. A solicitação é enviada para o Mediator, que processa a criação do usuário. Quando a resposta é recebida, ela é retornada como um resultado HTTP 200 OK.
A pasta Extensions contém métodos de extensão para IServiceCollection e IApplicatioinBuilder definidos nas classes estáticas : ApiBehaviorExtensions, CorsPolicyExtensions e ErrorHandlerExtensions :
1- ApiBehaviorExtensions
public
static
class
ApiBehaviorExtensions { public static void ConfigureApiBehavior(this IServiceCollection services) { services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; }); } } |
Esse código define um método de extensão para configurar o comportamento da API em uma aplicação ASP.NET Core.
O método de extensão ConfigureApiBehavior é um método de extensão que adiciona uma configuração específica ao comportamento da API na coleção de serviços (IServiceCollection). Ele aceita um parâmetro services, que é a instância da coleção de serviços onde a configuração será adicionada.
No código do método, a configuração do comportamento da API é definida usando a classe ApiBehaviorOptions que é configurada por meio do método Configure. O objetivo dessa configuração é suprimir o filtro que lida com os erros de validação de modelo (ModelState) na API. Isso significa que, se um modelo inválido for enviado em uma solicitação, os erros de validação do modelo não serão tratados automaticamente pelo filtro. Essa ação significa que a ASP.NET Core não tratará automaticamente os erros de validação do modelo, e você precisará lidar manualmente com a validação e os erros.
2- CorsPolicyExtensions
public
static
class
CorsPolicyExtensions { public static void ConfigureCorsPolicy(this IServiceCollection services) { services.AddCors(opt => { opt.AddDefaultPolicy(builder => builder .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); }); } } |
Esse código define um método de extensão para configurar políticas CORS (Cross-Origin Resource Sharing) em uma aplicação ASP.NET Core.
Esse é um método de extensão que adiciona uma configuração de política CORS à coleção de serviços (IServiceCollection). Ele aceita um parâmetro services, que é a instância da coleção de serviços onde a configuração será adicionada.
Nesse bloco, a configuração da política CORS é definida. A classe CorsOptions é configurada por meio do método AddCors. Isso define uma política CORS padrão que permite qualquer origem (AllowAnyOrigin), qualquer método HTTP (AllowAnyMethod) e qualquer cabeçalho (AllowAnyHeader).
3- ErrorHandlerExtensions
using
CleanArchitecture.Application.Shared.Exceptions; using Microsoft.AspNetCore.Diagnostics; using System.Net; using System.Text.Json; namespace CleanArchitecture.WebAPI.Extensions;public static class ErrorHandlerExtensions{ public static void UseErrorHandler(this IApplicationBuilder app) { app.UseExceptionHandler(appError => { appError.Run(async context => { var contextFeature = context.Features.Get<IExceptionHandlerFeature>(); if (contextFeature == null) return; context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.ContentType = "application/json"; context.Response.StatusCode = contextFeature.Error switch { BadRequestException => (int)HttpStatusCode.BadRequest, OperationCanceledException => (int)HttpStatusCode.ServiceUnavailable, NotFoundException => (int)HttpStatusCode.NotFound, _ => (int)HttpStatusCode.InternalServerError }; var errorResponse = new { statusCode = context.Response.StatusCode, message = contextFeature.Error.GetBaseException().Message }; await context.Response.WriteAsync(JsonSerializer.Serialize(errorResponse)); }); }); } } |
Esse código está importando os namespaces necessários para a implementação das extensões, incluindo namespaces relacionados a exceções, manipulação de erros, comunicação HTTP e serialização JSON.
Esse é um método de extensão que adiciona um manipulador de erro global à pipeline de solicitação. Ele aceita um parâmetro app, que é a instância da classe IApplicationBuilder na qual o manipulador de erro será configurado.
No
código temos que a configuração do manipulador de erro global é
definida usando o método UseExceptionHandler.
Dentro do manipulador, o contexto da solicitação é acessado por meio
da variável context.
O
context.Features.Get<IExceptionHandlerFeature>() obtém
informações sobre a exceção que ocorreu durante o processamento da
solicitação.
Depois disso, são feitas as seguintes ações:
Finalmente, a resposta é serializada em JSON e enviada de volta ao cliente.
No arquivo appsettings.json temos a definição da string de conexão para acessar o banco SQLite:
{ "ConnectionStrings": { "Sqlite": "Data Source=users.db" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } |
Na classe Program temos o registro dos serviços e a configuração do pipeline do request:
using
CleanArchitecture.Persistence.Context; using CleanArchitecture.WebAPI.Extensions; using CleanArchitecture.Application.Services; using CleanArchitecture.Persistence; var builder = WebApplication.CreateBuilder(args);// Add services to the container. builder.Services.ConfigurePersistenceApp(builder.Configuration); builder.Services.ConfigureApplicationApp(); builder.Services.ConfigureApiBehavior(); builder.Services.ConfigureCorsPolicy(); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build();var serviceScope = app.Services.CreateScope();var dataContext = serviceScope.ServiceProvider.GetService<AppDbContext>(); dataContext?.Database.EnsureCreated(); app.UseSwagger(); |
Explicando o código acima temos que :
A seguir é criado um escopo de serviço para a aplicação (serviceScope), e em seguida, o contexto do banco de dados (AppDbContext) é obtido do provedor de serviços. É verificado se o contexto existe e, se existir, o método EnsureCreated é chamado para garantir que o banco de dados seja criado se não existir.
Executando o projeto teremos a apresentação dos dois endpoints criados na interface do Swagger:
Após criar alguns usuários usando o endpoint POST /api/Users podemos obter os dados usando o método GET /api/Users:
Com isso temos uma implementação da Clean Architecture em um projeto Web API de forma objetiva e simples.
Em outro artigo vamos implementar os testes de unidade.
Pegue o código
aqui : CleanArchitecture.zip
"Ouve tu então nos céus, assento da tua habitação,
e perdoa, e age, e dá a cada um conforme a todos os seus caminhos, e segundo
vires o seu coração, porque só tu conheces o coração de todos os filhos dos
homens."
1 Reis 8:39
Referências:
C# - Tasks x Threads. Qual a diferença
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)