ASP.NET Core - FluentValidation em minimal APIs


  Hoje vamos recordar como usar a FluentValidation com minimal APIs na ASP.NET Core 7.

Atualmente não existe suporte para validar o modelo nas minimal APIs (como temos na ASP.NET Core). Portanto, precisamos escrever um código personalizado para validar os modelos em nossa aplicação.

Uma alternativa é usar os recursos da FluentValidation e é isso que veremos a seguir.

Criando a minimal API

Vamos criar um projeto Web API no VS 2022 chamado ApiValidation selecionando as opções a seguir:

 

Para utilizar a funcionalidade do FluentValidation, precisamos instalar a biblioteca FluentValidation do gerenciador de pacotes NuGet.

Podemos fazer isso na opção Tools ->..->Manage Nuget Packages for Solution e na guia Browse selecionar o pacote FluentValidation.AspNetCore :

Vamos cria no projeto a pasta Entities e criar nesta pasta a classe Produto que representa nosso modelo de domínio :

public class Produto
{
 
 public int Id { get; set; }
  
public string? Codigo { get; set; }
  
public int Quantidade { get; set; }
  
public decimal Preco {get; set; }
}

Como nosso objetivo é focar na validação vamos criar um serviço fake apenas para usarmos no endpoint da API . Para isso crie a pasta Services e nesta pasta crie a interface IProdutoService e a classe ProdutoService:

1- IProdutoService

public interface IProdutoService
{
  
void IncluirProduto(Produto produto);
}

2- ProdutoService

public class ProdutoService :IProdutoService
{
  
public void IncluirProduto(Produto produto)
   {
     
//lógica para incluir o produto no banco de dados
   }
}

Vamos criar uma pasta chamada Validations onde iremos definir as regras de validação usando a FluentValidation.  Nesta pasta crie a classe ProdutoValidator conforme o código abaixo:

using ApiValidation.Entities;
using
FluentValidation;

namespace ApiValidation.Validations;

public class ProdutoValidator : AbstractValidator<Produto>
{
 
public ProdutoValidator()
  {
    RuleFor(x => x.Id).NotNull();
    RuleFor(x => x.Codigo).Length(5);
    RuleFor(x => x.Quantidade).NotNull().InclusiveBetween(1,99);
    RuleFor(x => x.Preco).NotNull().InclusiveBetween(10.00m,50.00m);
   }
}

A classe ProdutoValidator herda da classe AbstractValidator, que é a classe base da biblioteca FluentValidation para criação de validadores. O tipo genérico <Produto> indica que este validador é específico para objetos do tipo Produto.

No construtor da classe ProdutoValidator, são definidas as regras de validação para cada propriedade do objeto Produto:

Essas regras de validação serão aplicadas quando um objeto Produto for validado através do método Validate da biblioteca FluentValidation.

Agora vamos registrar o validator e o serviço na classe Program:

...
builder.Services.AddScoped<IValidator<Produto>, ProdutoValidator>();

builder.Services.AddScoped<IProdutoService, ProdutoService>();
...

Com tudo pronto podemos criar um endpoint na classe Program:

app.MapPost("/produto", async (IValidator<Produto> validator, IProdutoService service, Produto produto) =>
{
    var validationResult = await validator.ValidateAsync(produto);

    if (!validationResult.IsValid)
    {
        return Results.ValidationProblem(validationResult.ToDictionary());
    }
    service.IncluirProduto(produto);
    return Results.Created($"/{produto.Id}", produto);
});
app.Run();

Este código define um endpoint HTTP POST que recebe um objeto Produto e o valida usando a biblioteca FluentValidation antes de incluí-lo no serviço de produtos.

Na linha

app.MapPost("/produto", async (IValidator<Produto> validator, IProdutoService service, Produto produto) =>{}

é definido um endpoint HTTP POST na rota "/produto". Este endpoint recebe três parâmetros:

Dentro do método anônimo, primeiro é realizada a validação do objeto Produto usando o método ValidateAsync do validador injetado. O resultado da validação é armazenado na variável validationResult.

Em seguida, é verificado se a validação falhou usando a propriedade IsValid do validationResult. Caso a validação tenha falhado, é retornado um objeto de problema de validação HTTP usando o método Results.ValidationProblem, que é uma extensão da classe HttpResponse. Esse objeto é construído a partir do dicionário de erros retornado pelo método ToDictionary do validationResult.

Se a validação for bem sucedida, o método IncluirProduto do serviço de produtos é chamado para incluir o objeto Produto no banco de dados. Finalmente, uma resposta HTTP 201 (Created) é retornada com o objeto Produto incluído no corpo da resposta, e o cabeçalho Location contendo o ID do produto recém-criado.

Executando o projeto e inserindo alguns dados para testar a validação obtemos o seguinte resultado:

Ao clicar em Execute obtemos o seguinte:

Este é um exemplo básico que mostra o uso da FluentValidation nas minimal APIs.

Você também pode aplicar validadores personalizados usando a interface IValidatableObject. Essa interface contém um método chamado Validate, que você mesmo precisaria implementar. Para fazer isso, você criaria uma classe de modelo que implementasse essa interface e o método Validate.

Pegue o projeto aqui : ApiValidation.zip...

"Louvai ao SENHOR. Louvai ao SENHOR desde os céus, louvai-o nas alturas.
Louvai-o, todos os seus anjos; louvai-o, todos os seus exércitos.
Louvai-o, sol e lua; louvai-o, todas as estrelas luzentes."
Salmos 148:1-3

Referências:


José Carlos Macoratti