Pular para o conteúdo
.NETModelState e atributos built-in
Módulo 04CRUD Completo e Validação

ModelState e atributos built-in

intermediario 35 min de leitura·Atualizado · .NET 9
Resumo

A validação de modelo do ASP.NET Core usa Data Annotations e o ModelState. Com [ApiController], respostas 400 para modelos inválidos são automáticas. Vemos atributos built-in e como criar atributos e objetos validáveis customizados.

1. Objetivos de Aprendizagem#

  • Usar Data Annotations ([Required], [Range], [StringLength]).
  • Entender ModelState e a validação automática do [ApiController].
  • Criar um ValidationAttribute customizado e usar IValidatableObject.

2. Pré-requisitos#

  • DTOs de criação/atualização (Módulos 09/11).

3. Conceito#

O ModelState acumula erros de binding e validação. Com [ApiController], se ModelState for inválido, o framework retorna 400 Bad Request com ValidationProblemDetails antes de a ação executar. O porquê: menos código repetido e respostas de erro consistentes.

4. Mão na Massa#

4.1. Setup#

Data Annotations já estão disponíveis (System.ComponentModel.DataAnnotations).

4.2. Implementação Passo a Passo#

  1. Atributos built-in no DTO:
C#
using System.ComponentModel.DataAnnotations;

public record ProdutoForCreationDto(
    [property: Required(ErrorMessage = "O nome é obrigatório.")]
    [property: StringLength(60, MinimumLength = 2)]
    string Nome,

    [property: Range(0.01, 1_000_000, ErrorMessage = "Preço deve ser positivo.")]
    decimal Preco);
  1. Atributo customizado:
C#
public class NomeSemNumerosAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext ctx)
    {
        if (value is string s && s.Any(char.IsDigit))
            return new ValidationResult("O nome não pode conter números.");
        return ValidationResult.Success;
    }
}
  1. IValidatableObject para validação com múltiplos campos:
C#
public class ProdutoForUpdateDto : IValidatableObject
{
    public string Nome { get; set; } = default!;
    public decimal Preco { get; set; }
    public decimal? PrecoPromocional { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext ctx)
    {
        if (PrecoPromocional is > 0 && PrecoPromocional >= Preco)
            yield return new ValidationResult(
                "Preço promocional deve ser menor que o preço.",
                new[] { nameof(PrecoPromocional) });
    }
}

4.3. Executando#

Shell
curl -i -X POST .../produtos -H "Content-Type: application/json" -d '{"nome":"","preco":-1}'
# HTTP/1.1 400 Bad Request  (ValidationProblemDetails com os erros)

5. Exemplo Completo#

Com [ApiController], nenhum if (!ModelState.IsValid) return BadRequest(...) é necessário para atributos — o framework cuida. Para validações após patch, use TryValidateModel (Módulo 26).

6. Boas Práticas e Armadilhas#

FaçaEvite
Validar na borda (DTO de entrada)Deixar dados inválidos chegarem ao domínio/banco
Mensagens de erro claras e específicasMensagens genéricas que não ajudam o cliente
Usar IValidatableObject para regras multi-campoEspalhar essa lógica pelo controller
Atenção

Data Annotations validam formato/estrutura, não regras de negócio complexas. Regras de negócio pertencem à Service Layer (ou FluentValidation, Módulo 44).

7. Segurança e Produção#

  • Validar limites de tamanho (StringLength, MaxLength) mitiga payloads abusivos.
  • Nunca confie apenas na validação do cliente; a validação no servidor é a que protege.

8. Exercícios#

  • Fácil: aplique [Required] e [Range] ao ProdutoForCreationDto.
  • Médio: crie um atributo que exija que o nome comece com maiúscula.
  • Desafio: implemente IValidatableObject que valide coerência entre três campos.

9. Resumo#

A validação usa Data Annotations e ModelState; [ApiController] gera 400 automático. Atributos customizados e IValidatableObject cobrem regras específicas na borda da aplicação.

10. Próximos Passos#

A seguir, particularidades da validação em POST, PUT e PATCH.

11. Referências#

27-01 — ModelState e atributos built-in | Curso ASP.NET Core