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.