NET
7
- Usando filtros em Mininal APIs
![]() |
Neste artigo vou mostrar como usar o novo recursos dos filtros das Minimal APIs na ASP.NET Core 7 para fazer a validação com Fluent Validation. |
Eu já apresentei o novo recursos dos filtros das minimal APIs neste artigo : NET 7 - Mininal APIs : Apresentando filtros
Hoje vamos criar uma minimal API no ambiente do .NET 7 e mostrar como usar os filtros para realizar a validação usando a Fluent Validation.
Recursos usados :
Criando o projeto no VS 2022 e configurando o ambiente
Abra o VS 2022 preview e clique em Create a New Project selecionando o template “ASP.NET Core Web API” :
Informe o nome ApiProdutos;
A seguir defina as demais configurações conforme mostrada na imagem a seguir:
Ao clicar em Create teremos a minimal API criada.
Vamos alterar o código gerado pelo template limpando o código da classe Program.
A seguir vamos incluir os seguintes pacotes no projeto:
Crie uma pasta Models no projeto e a seguir inclua nesta pasta a classe Produto:
public
class
Produto { public int Id { get; set; } [MaxLength(100)] public string? Nome { get; set; } [Column(TypeName = "decimal(10,2)")] public decimal Preco { get; set; } public int Estoque { get; set; } } |
Crie outra pasta Context no projeto e inclua nesta pasta o arquivo AppDbContext:
using
Microsoft.EntityFrameworkCore; namespace ApiProdutos.Data;public class AppDbContext : DbContext{ public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {} public DbSet<Produto> Produtos { get; set; } } |
Inclua no arquivo appsettings.json a string de conexão:
"ConnectionStrings":
{ "DefaultConnection": "Data Source=.;Initial Catalog=ProdutosDB;Integrated Security=True;TrustServerCertificate=True" }, ... |
Na classe Program vamos registrar o contexto:
var
builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddEndpointsApiExplorer(); ... |
A seguir aplique o migrations usando os comandos:
Add-Migration Inicial -c AppDbContext -o Context/Migrations
E a seguir :
Update-Database Inicial
Com isso teremos o banco de dados ProdutosDB criado a tabela Produtos.
Configurando a Fluent Validation
Crie uma pasta Validators no projeto a seguir inclua nesta pasta a classe ProdutoValidator:
using
ApiProdutos.Data; using FluentValidation; namespace ApiProdutos.Validators; public class ProdutoValidator : AbstractValidator<Produto> { public ProdutoValidator() { RuleFor(o => o.Nome).NotNull().NotEmpty().MinimumLength(3); RuleFor(o => o.Preco).NotNull().NotEmpty().NotEqual(0); RuleFor(o => o.Estoque).NotNull().NotEmpty().NotEqual(0); } } |
Como você pode ver, uma única invocação de método pode resultar em vários erros, portanto, vamos criar um método de extensão para combinar a saída em uma única mensagem de erro. Usaremos isso ao invocar a validação.
Assim crie a pasta Extensions e nesta pasta a classe ValidationErrorExtensions.cs:
using
FluentValidation.Results; namespace ApiProdutos.Extensions;public static class ValidationErrorExtensions{ public static string GetErrors(this List<ValidationFailure> errors) { var errorMessages = ""; errors.ForEach(err => errorMessages += err.ErrorMessage + ""); return errorMessages; } } |
Criando o filtro
Vamos usar agora o recurso dos filtros das minimal APIs criando a classe ValidationFilter genérica que deriva de IEndpointFilter.
using
ApiProdutos.Extensions; using FluentValidation;namespace ApiProdutos.Filters;public class ValidationFilter<T> : IEndpointFilter where T : class{ private readonly IValidator<T> _validator; public ValidationFilter(IValidator<T> validator) { _validator = validator; } public async ValueTask<object> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var parameter = context.Arguments.SingleOrDefault(p => p.GetType() == typeof(T)); if (parameter is null) return Results.BadRequest("O parametro é inválido."); var result = await _validator.ValidateAsync((T)parameter); if (!result.IsValid) { var errors = result.Errors.GetErrors(); return Results.Problem(errors); } // now the actual endpoint execution return await next(context); } } |
O tipo T na classe ValidationFilter será definida por nossa entidade Produto. Observe que a lógica do filtro ocorre antes de retornarmos await next(context). Até esse ponto, se algo der errado, ou seja: a validação falhar, o processo entra em curto-circuito ou seja, não é executado.
Criando os endpoints
Vamos criar agora uma classe separada para os endpoints de nossos produtos e
assim não vamos incluir todos os endpoints em Program.cs, mas em um
arquivo separado na pasta Endpoints na classe
ProdutosEndpoints.cs. que uma classe de extensão de WebApplication.
Assim crie a pasta Endpoints e nesta pasta a classe estática ProdutosEndpoints:
using ApiProdutos.Data; using ApiProdutos.Extensions; using ApiProdutos.Filters; using ApiProdutos.Validators; using FluentValidation; using Microsoft.EntityFrameworkCore; using System; using static Microsoft.AspNetCore.Http.Results; namespace ApiProdutos.Endpoints; public static class ProdutosEndpoints public static async Task<IResult> List(AppDbContext db) public static async Task<IResult> Get(AppDbContext db, int id) public static async Task<IResult> Create(AppDbContext db, return Results.Created($"/produtos/{produto.Id}", produto); public static async Task<IResult> Update(AppDbContext db,
if (produto is null) return Results.NotFound(); produto.Nome = produtoAtualizado.Nome; await db.SaveChangesAsync(); public static async Task<IResult> Delete(AppDbContext db, IValidator<Produto> validator, int id) |
Na primeira parte temos um método estático chamado MapProdutosEndpoints que contém o app.MapGet, app.MapPost, etc. Cada uma dessas funções recebe um delegate (a funcionalidade real do endpoint). Todas essas funções, List, Get, Create, ficam abaixo desse método.
Agora vamos registrar a validador e os endpoints na classe Program:
var
builder = WebApplication.CreateBuilder(args); // Add services to the container.
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddDbContext<AppDbContext>(options
=>options.UseSqlServer builder.Services.AddScoped<IValidator<Produto>, ProdutoValidator>(); var app = builder.Build();// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI(); } app.MapProdutosEndpoints(); app.UseHttpsRedirection(); |
Executando o projeto teremos os endpoints exibidos na interface do Swagger:
Vamos testar a validação acionando o endpoint Post /produtos e tentar criar um produto sem os dados válidos:
Acionando o botão Execute teremos o seguinte response:
Observe as mensagens de validação indicando que o filtro esta funcionando.
E estamos
conversados...
Pegue o projeto
aqui :
ApiProdutos.zip
"como está escrito:
Não há justo, nem um sequer, não há quem entenda, não há quem busque a Deus;
todos se extraviaram, à uma se fizeram inúteis; não há quem faça o bem, não
há nem um sequer."
Romanos 3:10-12
Referências:
Curso Fundamentos da Programação Orientada a Objetos com VB .NET