Implementando CRUD Completo em Web API com C# .NET

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.