No desenvolvimento de APIs com .NET, é essencial implementar operações completas de CRUD (Create, Read, Update, Delete). Este artigo explora como criar endpoints robustos para manipulação de dados, desde consultas simples até atualizações complexas.
Criando a Entidade Cliente
Para operações completas, precisamos de uma entidade bem definida. Vamos criar uma classe Cliente na pasta Models:
public class Cliente { public int Id { get; set; } public string Nome { get; set; } = string.Empty; public string Endereco { get; set; } = string.Empty; public DateOnly DataNascimento { get; set; } public Cliente(int id, string nome, string endereco, DateOnly dataNascimento) { Id = id; Nome = nome; Endereco = endereco; DataNascimento = dataNascimento; } }
Implementando o Controller com Operações CRUD
Vamos criar um controller completo com todas as operações necessárias:
[ApiController] [Route("api/[controller]")] public class ClientesController : ControllerBase { private static List<Cliente> _clientes = new() { new Cliente(1, "Maria", "Centro", DateOnly.FromDateTime(DateTime.Now.AddDays(-1000))), new Cliente(2, "Fernando", "Praça", DateOnly.FromDateTime(DateTime.Now.AddDays(-1002))), new Cliente(3, "Carlos", "Rua Principal", DateOnly.FromDateTime(DateTime.Now.AddDays(-1003))) }; // GET: api/clientes [HttpGet] public IActionResult GetAll() { return Ok(_clientes); } // GET api/clientes/5 [HttpGet("{id}")] public IActionResult GetById(int id) { var cliente = _clientes.FirstOrDefault(c => c.Id == id); if (cliente == null) return NotFound(); return Ok(cliente); } // POST api/clientes [HttpPost] public IActionResult Create([FromBody] Cliente cliente) { if (_clientes.Any(c => c.Id == cliente.Id)) return Conflict("Já existe um cliente com este ID"); _clientes.Add(cliente); return CreatedAtAction(nameof(GetById), new { id = cliente.Id }, cliente); } // PUT api/clientes/5 [HttpPut("{id}")] public IActionResult Update(int id, [FromBody] Cliente clienteUpdate) { var clienteExistente = _clientes.FirstOrDefault(c => c.Id == id); if (clienteExistente == null) return NotFound(); clienteExistente.Nome = clienteUpdate.Nome; clienteExistente.Endereco = clienteUpdate.Endereco; clienteExistente.DataNascimento = clienteUpdate.DataNascimento; return NoContent(); } // DELETE api/clientes/5 [HttpDelete("{id}")] public IActionResult Delete(int id) { var cliente = _clientes.FirstOrDefault(c => c.Id == id); if (cliente == null) return NotFound(); _clientes.Remove(cliente); return NoContent(); } }
Métodos HTTP e Suas Finalidades
Cada método HTTP tem um propósito específico no CRUD:
- GET: Recuperar dados (Read)
- POST: Criar novos recursos (Create)
- PUT: Atualizar recursos existentes (Update)
- DELETE: Remover recursos (Delete)
Tratamento de Erros e Respostas HTTP
É crucial retornar códigos HTTP apropriados para diferentes cenários:
// Exemplo de tratamento completo de erros [HttpPut("{id}")] public IActionResult Update(int id, [FromBody] Cliente clienteUpdate) { try { if (clienteUpdate == null) return BadRequest("Dados do cliente inválidos"); if (id != clienteUpdate.Id) return BadRequest("ID inconsistente"); var clienteExistente = _clientes.FirstOrDefault(c => c.Id == id); if (clienteExistente == null) return NotFound($"Cliente com ID {id} não encontrado"); // Atualiza propriedades clienteExistente.Nome = clienteUpdate.Nome; clienteExistente.Endereco = clienteUpdate.Endereco; clienteExistente.DataNascimento = clienteUpdate.DataNascimento; return NoContent(); } catch (Exception ex) { return StatusCode(500, $"Erro interno: {ex.Message}"); } }
Validação de Dados
A validação adequada é essencial para uma API robusta:
public class Cliente { public int Id { get; set; } [Required] [StringLength(100, MinimumLength = 2)] public string Nome { get; set; } = string.Empty; [StringLength(200)] public string Endereco { get; set; } = string.Empty; [DataType(DataType.Date)] public DateOnly DataNascimento { get; set; } } // No Controller [HttpPost] public IActionResult Create([FromBody] Cliente cliente) { if (!ModelState.IsValid) return BadRequest(ModelState); // Resto da lógica... }
Considerações sobre Persistência
É importante entender as limitações do armazenamento em memória:
- Dados em memória são voláteis e se perdem ao reiniciar a aplicação
- Para persistência real, implemente um repositório com banco de dados
- Considere usar Entity Framework Core para operações de banco
- Implemente padrão Repository para abstração da camada de dados
Melhores Práticas para APIs RESTful
Siga estas práticas recomendadas para APIs robustas:
- Use substantivos no plural para endpoints (ex: /api/clientes)
- Retorne códigos HTTP apropriados para cada situação
- Implemente versionamento da API
- Use DTOs (Data Transfer Objects) para transferência de dados
- Implemente autenticação e autorização adequadas
- Adicione documentação com Swagger/OpenAPI
- Implemente paginação para endpoints que retornam listas grandes
- Use caching onde apropriado
Conclusão
A implementação de um CRUD completo é fundamental para qualquer API RESTful. Comece com operações básicas em memória e evolua para soluções mais robustas com banco de dados, validação avançada e tratamento completo de erros.
Lembre-se de sempre seguir as convenções REST, retornar códigos HTTP apropriados e documentar sua API adequadamente. Essas práticas garantem que sua API seja fácil de usar, manter e integrar com outros sistemas.
Próximos passos: Após dominar o CRUD básico, explore tópicos avançados como relacionamentos entre entidades, queries complexas, transações, logging, monitoramento e deploy em ambientes de produção.