Validação em POST, PUT e PATCH
ResumoCada verbo tem nuances de validação. Padronizamos o uso de
400para erros de formato e422 Unprocessable Entitypara conteúdo semanticamente inválido, e cuidamos da revalidação noPATCH.
1. Objetivos de Aprendizagem#
- Aplicar validação consistente em
POST,PUTePATCH. - Distinguir
400 Bad Requestde422 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:
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
new UnprocessableEntityObjectResult(context.ModelState);
});
4.2. Implementação Passo a Passo#
POST/PUT: com[ApiController]e a config acima, DTOs inválidos já retornam422.PATCH: a validação precisa ser manual apósApplyTo:
patchDoc.ApplyTo(dto, ModelState);
if (!TryValidateModel(dto))
return UnprocessableEntity(ModelState);
- Regras de negócio (não estruturais) na Service Layer:
if (await _repo.Produto.ExisteComNomeAsync(dto.Nome))
throw new ProdutoJaExisteException(dto.Nome); // handler global → 409/422
4.3. Executando#
# 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:
| Verbo | Erro de formato | Erro de conteúdo | Revalidação manual? |
|---|---|---|---|
| POST | 400 | 422 | Não (automática) |
| PUT | 400 | 422 | Não (automática) |
| PATCH | 400 | 422 | Sim (TryValidateModel) |
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Padronizar 400 x 422 em toda a API | Misturar códigos sem critério |
Revalidar o DTO após ApplyTo no PATCH | Confiar na validação automática (não roda no PATCH) |
| Colocar regras de negócio no serviço | Sobrecarregar Data Annotations com regras de domínio |
Atençãoa validação automática do
[ApiController]não dispara para oJsonPatchDocument, pois a validação ocorre antes doApplyTo. Por isso oTryValidateModelé 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
422em vez de400para 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#
- Microsoft Learn — Validação de modelo
- Microsoft Learn — ApiBehaviorOptions
- MDN — 422 Unprocessable Content