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.