Pular para o conteúdo
.NETValidação em POST, PUT e PATCH
Módulo 04CRUD Completo e Validação

Validação em POST, PUT e PATCH

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

Cada verbo tem nuances de validação. Padronizamos o uso de 400 para erros de formato e 422 Unprocessable Entity para conteúdo semanticamente inválido, e cuidamos da revalidação no PATCH.

1. Objetivos de Aprendizagem#

  • Aplicar validação consistente em POST, PUT e PATCH.
  • Distinguir 400 Bad Request de 422 Unprocessable Entity.
  • Revalidar corretamente após aplicar um JSON Patch.

2. Pré-requisitos#

  • ModelState e atributos (tópico 27.1); PATCH (Módulo 26).

3. Conceito#

  • 400 Bad Request — a requisição está malformada (JSON inválido, tipo errado).
  • 422 Unprocessable Entity — a sintaxe está correta, mas o conteúdo viola regras (ex.: preço negativo).

O porquê da distinção: dá ao cliente informação precisa sobre a natureza do erro. Muitas APIs usam 400 para ambos; adotar 422 para validação semântica é uma convenção mais expressiva.

4. Mão na Massa#

4.1. Setup#

Opcional: configure o [ApiController] para usar 422 em falhas de validação:

C#
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
        new UnprocessableEntityObjectResult(context.ModelState);
});

4.2. Implementação Passo a Passo#

  1. POST/PUT: com [ApiController] e a config acima, DTOs inválidos já retornam 422.
  2. PATCH: a validação precisa ser manual após ApplyTo:
C#
patchDoc.ApplyTo(dto, ModelState);
if (!TryValidateModel(dto))
    return UnprocessableEntity(ModelState);
  1. Regras de negócio (não estruturais) na Service Layer:
C#
if (await _repo.Produto.ExisteComNomeAsync(dto.Nome))
    throw new ProdutoJaExisteException(dto.Nome); // handler global → 409/422

4.3. Executando#

Shell
# 400: JSON malformado
curl -i -X POST .../produtos -d '{ isso não é json }'
# 422: JSON válido, conteúdo inválido
curl -i -X POST .../produtos -H "Content-Type: application/json" -d '{"nome":"x","preco":-5}'

5. Exemplo Completo#

Matriz de comportamento:

VerboErro de formatoErro de conteúdoRevalidação manual?
POST400422Não (automática)
PUT400422Não (automática)
PATCH400422Sim (TryValidateModel)

6. Boas Práticas e Armadilhas#

FaçaEvite
Padronizar 400 x 422 em toda a APIMisturar códigos sem critério
Revalidar o DTO após ApplyTo no PATCHConfiar na validação automática (não roda no PATCH)
Colocar regras de negócio no serviçoSobrecarregar Data Annotations com regras de domínio
Atenção

a validação automática do [ApiController] não dispara para o JsonPatchDocument, pois a validação ocorre antes do ApplyTo. Por isso o TryValidateModel é obrigatório no PATCH.

7. Segurança e Produção#

  • Mensagens de validação não devem revelar detalhes internos (nomes de colunas, queries).
  • Padronize o corpo de erro (ValidationProblemDetails) para clientes tratarem de forma uniforme.

8. Exercícios#

  • Fácil: configure a API para retornar 422 em vez de 400 para validação.
  • Médio: crie uma regra de negócio "nome único" no serviço e mapeie para 409 Conflict.
  • Desafio: unifique o formato de erro entre validação de modelo e exceções de domínio.

9. Resumo#

Padronizamos 400 (formato) e 422 (conteúdo) entre os verbos, lembrando que o PATCH exige revalidação manual e que regras de negócio pertencem à Service Layer.

10. Próximos Passos#

Módulo 28: tornar toda a pilha assíncrona com async/await.

11. Referências#

27-02 — Validação em POST, PUT e PATCH | Curso ASP.NET Core