No desenvolvimento de software, validar os objetos de entrada é essencial para garantir a integridade dos dados e detectar problemas desde as fases iniciais. Nesse cenário, o FluentValidator se destaca como uma biblioteca amplamente utilizada para realizar a validação de modelos de forma eficiente e estruturada.
Neste exemplo, construiremos uma API em .NET 8 com a responsabilidade de cadastrar pessoas. Para isso, começaremos definindo um modelo Person com alguns campos:
public record Person
{
public string Name { get; set; } = string.Empty!;
public string Document{ get; set; } = string.Empty!;
public string Email { get; set; } = string.Empty!;
public int Age { get; set; }
}
Com o modelo definido, é hora de adicionar o pacote FluentValidation ao projeto. Para isso, execute o comando abaixo em seu projeto:
dotnet add package FluentValidation.AspNetCore
Agora podemos começar a implementar as validações. Para isso, crie uma pasta chamada Validators e, dentro dela, adicione a classe PersonValidators:
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.Name).Length(0, 100)
.WithMessage("O Nome é obrigatório");
RuleFor(x => x.Email).EmailAddress()
.WithMessage("Endereço de e-mail inválido");
RuleFor(x => x.Age).InclusiveBetween(18, 60)
.WithMessage("Deve ter entre 18 e 60 anos");
RuleFor(x => x.Document).Length(11)
.WithMessage("CPF inválido, deve ter 11 digitos.");
}
}
Após implementar a classe com as regras de validação, é necessário registrar o validador no ServiceProvider. Em seguida, resolva sua dependência para que o .NET possa reconhecer e executar as validações. Existem duas maneiras de registrar validadores ao ServiceProvider. A primeira é registrá-los individualmente na classe Program:
builder.Services.AddScoped<IValidator<Person>, PersonValidator>();
No entanto, se o seu projeto tiver muitos validadores, utilizar essa abordagem se torna inviável e aumenta as chances de erros. Logo, podemos utilizar a abordagem de registrar todos os validadores de uma só vez no mesmo assembly PersonValidator.
builder.Services.AddValidatorsFromAssemblyContaining<PersonValidator>();
Com os validadores registrados, vamos implementar o trecho de código que realiza as validações e identifica possíveis erros. Veja abaixo como ficou a implementação:
public class PersonController : ControllerBase
{
private IValidator<Person> _validator;
public PersonController(IValidator<Person> validator) =>
_validator = validator;
[HttpPost(Name = "PostPerson")]
public IActionResult Post(Person person)
{
var validationResult = _validator.Validate(person);
if(validationResult.IsValid is false)
{
var errors = validationResult.Errors.Select(_ => _.ErrorMessage);
return BadRequest(errors);
}
return Ok();
}
}
Observe que o código responsável por validar o objeto de entrada está localizado dentro do método do controller. Optamos por essa abordagem para manter a implementação simples, sem isolar essa responsabilidade em um serviço separado. No entanto, uma melhoria seria permitir que o próprio modelo Person se validasse. Isso permitiria centralizar as regras de negócio no próprio modelo. Para isso, adicione o método Validate na classe Person:
public (bool isValid, IEnumerable<string> Errors) Validate(IValidator<Person> validator)
{
var validationResult = validator.Validate(this);
return (validationResult.IsValid, validationResult.Errors.Select(_ => _.ErrorMessage));
}
Observe que o método retorna uma tupla indicando se o objeto é válido, caso seja inválido, ele também retorna uma lista com os erros. Agora vamos ajustar o controller para utilizar esse método:
public class PersonController : ControllerBase
{
private IValidator<Person> _validator;
public PersonController(IValidator<Person> validator) =>
_validator = validator;
[HttpPost(Name = "PostPerson")]
public IActionResult Post(Person person)
{
var validationResult = person.Validate(_validator);
if(validationResult.isValid is false)
return BadRequest(validationResult.Errors);
return Ok();
}
}
Dessa forma isolamos as regras de validações dentro do modelo de entrada e eliminamos essa responsabilidade do controller.
Com isso cumprimos o objetivo do artigo que era trazer uma visão geral dessa biblioteca de forma prática para resolver problemas de validações de modelos.
No próximo artigo iremos implementar essas verificações através de Filters. Pois, sempre que uma requisição for solicitada à API, o Filter será acionado e fará a validação dos objetos de entrada. Nessa etapa a aplicação decide se deve prosseguir ou retornar os erros para o cliente.
Código fonte disponível no Github/Llimaa: DemoFluentValidation.
Top comments (0)