Tratando requisições POST
Resumo
POSTcria um novo recurso. A resposta correta é201 Createdcom o cabeçalhoLocationapontando para o recurso recém-criado — feito de forma limpa comCreatedAtRoute.
1. Objetivos de Aprendizagem#
- Implementar a criação de recurso com
POST. - Retornar
201 CreatedcomLocationcorreto. - 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#
- DTO de entrada:
public record ProdutoForCreationDto(string Nome, decimal Preco);
- Serviço de criação:
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);
}
- Controller:
[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#
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ça | Evite |
|---|---|
Retornar 201 + Location via CreatedAtRoute | Retornar 200 OK sem indicar a URL do recurso |
| Usar DTO de criação enxuto | Aceitar a entidade e permitir over-posting |
| Validar o pai antes de criar o filho | Criar produto em categoria inexistente |
Atençãocom
[ApiController], um corpo nulo já gera400automaticamente; não é necessário checardto is nullmanualmente (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,CategoriaIdfora da rota). - Considere limites de tamanho de payload para evitar abuso.
8. Exercícios#
- Fácil: crie
CategoriaForCreationDtoe o endpointPOST /api/categorias. - Médio: retorne
422 Unprocessable Entitypara 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#
- Microsoft Learn — Criar APIs web — POST
- Microsoft Learn — Tipos de retorno de ações
- MDN — 201 Created