Pular para o conteúdo
.NETTratando requisições POST
Módulo 04CRUD Completo e Validação

Tratando requisições POST

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

POST cria um novo recurso. A resposta correta é 201 Created com o cabeçalho Location apontando para o recurso recém-criado — feito de forma limpa com CreatedAtRoute.

1. Objetivos de Aprendizagem#

  • Implementar a criação de recurso com POST.
  • Retornar 201 Created com Location correto.
  • Usar um DTO de entrada separado do de leitura.

2. Pré-requisitos#

  • DTOs e AutoMapper (Módulo 18); rotas nomeadas (Módulo 20).

3. Conceito#

POST não é idempotente: cada chamada cria um recurso. A resposta idiomática é 201 Created, com Location indicando onde buscar o recurso criado. O porquê: o cliente descobre a URL do novo recurso sem adivinhar. Usamos um DTO de criação (ProdutoForCreationDto) que expõe apenas os campos de entrada, evitando over-posting.

4. Mão na Massa#

4.1. Setup#

Adicione o mapeamento ProdutoForCreationDto → Produto no perfil do AutoMapper.

4.2. Implementação Passo a Passo#

  1. DTO de entrada:
C#
public record ProdutoForCreationDto(string Nome, decimal Preco);
  1. Serviço de criação:
C#
public async Task<ProdutoDto> CreateAsync(Guid categoriaId, ProdutoForCreationDto dto, bool trackChanges)
{
    _ = await _repo.Categoria.GetByIdAsync(categoriaId, trackChanges)
        ?? throw new CategoriaNotFoundException(categoriaId);

    var entity = _mapper.Map<Produto>(dto);
    entity.CategoriaId = categoriaId;

    _repo.Produto.Create(entity);
    await _repo.SaveAsync();

    return _mapper.Map<ProdutoDto>(entity);
}
  1. Controller:
C#
[HttpPost]
public async Task<IActionResult> Criar(Guid categoriaId, [FromBody] ProdutoForCreationDto dto)
{
    var criado = await _service.Produto.CreateAsync(categoriaId, dto, trackChanges: false);
    return CreatedAtRoute("ProdutoById", new { categoriaId, id = criado.Id }, criado);
}

4.3. Executando#

Shell
curl -i -X POST http://localhost:5000/api/categorias/{catId}/produtos \
  -H "Content-Type: application/json" \
  -d '{"nome":"Mouse","preco":99.90}'
# HTTP/1.1 201 Created   Location: .../produtos/{novoId}

5. Exemplo Completo#

O CreatedAtRoute usa a rota nomeada ProdutoById (Módulo 20) para montar o Location, mantendo consistência com o GET de recurso único.

6. Boas Práticas e Armadilhas#

FaçaEvite
Retornar 201 + Location via CreatedAtRouteRetornar 200 OK sem indicar a URL do recurso
Usar DTO de criação enxutoAceitar a entidade e permitir over-posting
Validar o pai antes de criar o filhoCriar produto em categoria inexistente
Atenção

com [ApiController], um corpo nulo já gera 400 automaticamente; não é necessário checar dto is null manualmente (mas veja o Módulo 27 sobre validação de conteúdo).

7. Segurança e Produção#

  • DTO de criação previne que o cliente defina campos indevidos (ex.: Id, CategoriaId fora da rota).
  • Considere limites de tamanho de payload para evitar abuso.

8. Exercícios#

  • Fácil: crie CategoriaForCreationDto e o endpoint POST /api/categorias.
  • Médio: retorne 422 Unprocessable Entity para uma regra de negócio violada.
  • Desafio: adicione uma chave de idempotência ao endpoint de criação.

9. Resumo#

POST cria recursos e deve responder 201 Created com Location. DTOs de criação evitam over-posting e a validação do pai garante integridade.

10. Próximos Passos#

A seguir: criar recursos filhos junto do pai e coleções de recursos.

11. Referências#

23-01 — Tratando requisições POST | Curso ASP.NET Core