Neste artigo, vamos explorar a implementação completa de operações CRUD (Create, Read, Update, Delete) em uma API .NET, com foco especial na operação de DELETE e nas melhores práticas para desenvolvimento de APIs RESTful robustas e escaláveis.
Estrutura Básica do Controller
Vamos começar organizando nosso controller com os métodos essenciais do CRUD:
[ApiController] [Route("api/[controller]")] public class WeatherForecastController : ControllerBase { // GET: api/weatherforecast [HttpGet] public IActionResult GetAll() { // Retornar lista com todos os registros return Ok(clientes); } // GET api/weatherforecast/5 [HttpGet("{codigo}")] public IActionResult GetByCode(string codigo) { // Buscar item específico pelo código var item = clientes.FirstOrDefault(c => c.Codigo == codigo); if (item == null) return NotFound(); return Ok(item); } // PUT api/weatherforecast [HttpPut] public IActionResult Update([FromBody] Cliente clienteUpdate) { // Lógica de atualização aqui return Ok(true); } // DELETE api/weatherforecast/5 [HttpDelete("{codigo}")] public IActionResult Delete(string codigo) { // Lógica de exclusão aqui return Ok(true); } }
Implementando o Método DELETE
O método DELETE deve seguir boas práticas para garantir uma operação segura e informativa:
[HttpDelete("{codigo}")] public async Task<IActionResult> Delete(string codigo) { try { // Validar entrada if (string.IsNullOrEmpty(codigo)) return BadRequest("Código é obrigatório"); // Buscar o item no banco de dados var cliente = await _context.Clientes .FirstOrDefaultAsync(c => c.Codigo == codigo); if (cliente == null) return NotFound($"Cliente com código {codigo} não encontrado"); // Verificar dependências (se aplicável) var hasDependencies = await _context.Pedidos .AnyAsync(p => p.ClienteCodigo == codigo); if (hasDependencies) return Conflict("Não é possível excluir: existem pedidos associados"); // Executar exclusão _context.Clientes.Remove(cliente); await _context.SaveChangesAsync(); return NoContent(); // 204 No Content é o padrão para DELETE bem-sucedido } catch (Exception ex) { // Log do erro _logger.LogError(ex, "Erro ao excluir cliente {Codigo}", codigo); return StatusCode(500, "Erro interno do servidor"); } }
Tipos de Retorno Adequados
É crucial retornar os códigos HTTP apropriados para cada cenário:
- 200 OK: Operação bem-sucedida com conteúdo na resposta
- 204 No Content: DELETE bem-sucedido (sem conteúdo de retorno)
- 400 Bad Request: Dados de entrada inválidos
- 404 Not Found: Recurso não encontrado
- 409 Conflict: Conflito (ex: dependências existentes)
- 500 Internal Server Error: Erro interno do servidor
Validação e Tratamento de Erros
Implemente validações robustas para garantir a integridade dos dados:
public class Cliente { [Required] [StringLength(20, MinimumLength = 1)] public string Codigo { get; set; } [Required] [StringLength(100, MinimumLength = 2)] public string Nome { get; set; } [EmailAddress] public string Email { get; set; } } // No Controller [HttpPost] public IActionResult Create([FromBody] Cliente cliente) { if (!ModelState.IsValid) return BadRequest(ModelState); // Resto da lógica... }
Padrões de Nomenclatura e Rotas
Siga as convenções REST para URLs consistentes e intuitivas:
- GET /api/clientes: Listar todos
- GET /api/clientes/{id}: Buscar por ID
- POST /api/clientes: Criar novo
- PUT /api/clientes/{id}: Atualizar completo
- PATCH /api/clientes/{id}: Atualizar parcial
- DELETE /api/clientes/{id}: Excluir
Segurança e Autenticação
Proteja sua API com autenticação adequada:
[Authorize] [ApiController] public class ClientesController : ControllerBase { // Todos os endpoints exigem autenticação } // Ou para endpoints específicos [HttpDelete("{id}")] [Authorize(Roles = "Administrador")] public IActionResult Delete(int id) { // Apenas administradores podem excluir }
Logging e Monitoramento
Implemente logging comprehensive para debugging e monitoramento:
public class ClientesController : ControllerBase { private readonly ILogger<ClientesController> _logger; public ClientesController(ILogger<ClientesController> logger) { _logger = logger; } [HttpDelete("{id}")] public IActionResult Delete(int id) { _logger.LogInformation("Tentativa de exclusão do cliente {ClienteId}", id); try { // Lógica de exclusão _logger.LogInformation("Cliente {ClienteId} excluído com sucesso", id); return NoContent(); } catch (Exception ex) { _logger.LogError(ex, "Erro ao excluir cliente {ClienteId}", id); return StatusCode(500); } } }
Versionamento da API
Considere implementar versionamento para compatibilidade:
[ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class ClientesController : ControllerBase { // Endpoints da versão 1.0 } [ApiVersion("2.0")] [Route("api/v{version:apiVersion}/[controller]")] public class ClientesV2Controller : ControllerBase { // Endpoints da versão 2.0 }
Documentação com Swagger/OpenAPI
Documente sua API automaticamente com Swagger:
// Configure no Startup.cs ou Program.cs services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Minha API", Version = "v1", Description = "API para gerenciamento de clientes" }); }); // Use no pipeline app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Minha API V1"); });
Testes Automatizados
Implemente testes para garantir a qualidade do código:
[Test] public async Task Delete_ClienteExistente_RetornaNoContent() { // Arrange var cliente = new Cliente { Id = 1, Nome = "Teste" }; _mockContext.Setup(c => c.Clientes.FindAsync(1)) .ReturnsAsync(cliente); // Act var result = await _controller.Delete(1); // Assert Assert.IsType<NoContentResult>(result); _mockContext.Verify(c => c.Remove(cliente), Times.Once); _mockContext.Verify(c => c.SaveChangesAsync(), Times.Once); }
Considerações de Performance
Otimize sua API para melhor performance:
- Use operações assíncronas (async/await)
- Implemente paginação para endpoints de listagem
- Considere cache para dados frequentemente acessados
- Use compression para respostas grandes
- Monitor performance com Application Insights ou similar
Conclusão
Implementar um CRUD completo vai além de simples operações de banco de dados. Envolve considerações de segurança, validação, tratamento de erros, documentação e monitoramento. Seguindo estas práticas recomendadas, você estará construindo APIs robustas, escaláveis e maintainable.
Próximos passos: Após dominar o CRUD básico, explore tópicos avançados como relacionamentos complexos, transactions, unit of work pattern, repository pattern, e implementação de padrões como CQRS e MediaTR.
Lembre-se que uma API bem projetada não apenas funciona corretamente, mas também é intuitiva para consumir, bem documentada, segura e preparada para escalar conforme necessário.