Neste artigo, vamos explorar como criar uma API RESTful completa em .NET 8 usando Minimal APIs, implementando um CRUD (Create, Read, Update, Delete) completo para cadastro de pessoas. Além disso, vamos integrar OpenAPI/Swagger para documentação automática e Redoc para uma interface de documentação interativa e elegante.
O projeto demonstra as melhores práticas modernas de desenvolvimento .NET, incluindo:
- Minimal APIs para endpoints limpos e diretos
- Validação de dados com Data Annotations
- Documentação automática com OpenAPI 3.0
- Interface de documentação com Redoc
- Arquitetura em camadas com serviços e modelos separados
🛠️ Tecnologias Utilizadas
- .NET 8.0 - Framework moderno e de alto desempenho
- Minimal APIs - Abordagem simplificada para criar APIs REST
- Swashbuckle.AspNetCore - Geração automática de documentação OpenAPI/Swagger
- Redoc - Interface de documentação interativa e elegante
- Data Annotations - Validação de dados nativa do .NET
📁 Estrutura do Projeto
NR.Api/
├── Models/
│ ├── Pessoa.cs # Modelo de domínio
│ ├── CriarPessoaRequest.cs # DTO para criação
│ └── AtualizarPessoaRequest.cs # DTO para atualização
├── Services/
│ ├── IPessoaService.cs # Interface do serviço
│ └── PessoaService.cs # Implementação do serviço (em memória)
├── Properties/
│ └── launchSettings.json # Configurações de execução
├── wwwroot/
│ ├── index.html # Página inicial
│ └── redoc.html # Interface Redoc
├── Program.cs # Configuração e endpoints
└── NR.Api.csproj # Arquivo do projeto
🚀 Como Executar o Projeto
Pré-requisitos
- .NET 8 SDK instalado
- Visual Studio 2022, VS Code ou Rider (opcional)
Passos para Executar
- Clone ou navegue até o diretório do projeto
cd NR.Api
- Restaura as dependências
dotnet restore
- Execute a aplicação
dotnet run
- Acesse a documentação
- Redoc: http://localhost:5059/
- Swagger UI: http://localhost:5059/swagger (apenas em Development)
📚 Modelos de Dados
Pessoa (Modelo de Domínio)
public class Pessoa
{
public Guid Id { get; set; }
public string Nome { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Telefone { get; set; } = string.Empty;
public DateTime DataNascimento { get; set; }
public string? Endereco { get; set; }
public DateTime DataCriacao { get; set; }
public DateTime? DataAtualizacao { get; set; }
}
CriarPessoaRequest (DTO de Criação)
public class CriarPessoaRequest
{
[Required(ErrorMessage = "O nome é obrigatório")]
[StringLength(200, MinimumLength = 2)]
public string Nome { get; set; } = string.Empty;
[Required(ErrorMessage = "O email é obrigatório")]
[EmailAddress(ErrorMessage = "O email deve ser válido")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "O telefone é obrigatório")]
[StringLength(20, MinimumLength = 10)]
public string Telefone { get; set; } = string.Empty;
[Required(ErrorMessage = "A data de nascimento é obrigatória")]
public DateTime DataNascimento { get; set; }
[StringLength(500)]
public string? Endereco { get; set; }
}
AtualizarPessoaRequest (DTO de Atualização)
Similar ao CriarPessoaRequest, usado para atualização de dados.
🔌 Endpoints da API
1. Health Check
GET /health
Retorna o status de saúde da aplicação.
Resposta:
{
"status": "healthy",
"timestamp": "2024-01-09T10:30:00Z",
"version": "1.0.0"
}
2. Listar Todas as Pessoas
GET /api/pessoas
Retorna uma lista com todas as pessoas cadastradas.
Resposta (200 OK):
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"nome": "João Silva",
"email": "joao.silva@email.com",
"telefone": "(11) 98765-4321",
"dataNascimento": "1990-05-15T00:00:00Z",
"endereco": "Rua Exemplo, 123",
"dataCriacao": "2024-01-09T10:00:00Z",
"dataAtualizacao": null
}
]
3. Obter Pessoa por ID
GET /api/pessoas/{id}
Retorna os dados de uma pessoa específica.
Parâmetros:
-
id(Guid): ID único da pessoa
Resposta (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"nome": "João Silva",
"email": "joao.silva@email.com",
"telefone": "(11) 98765-4321",
"dataNascimento": "1990-05-15T00:00:00Z",
"endereco": "Rua Exemplo, 123",
"dataCriacao": "2024-01-09T10:00:00Z",
"dataAtualizacao": null
}
Resposta (404 Not Found):
{
"message": "Pessoa com ID {id} não encontrada"
}
4. Criar Nova Pessoa
POST /api/pessoas
Cria uma nova pessoa no sistema.
Body (JSON):
{
"nome": "Maria Santos",
"email": "maria.santos@email.com",
"telefone": "(11) 91234-5678",
"dataNascimento": "1985-08-20",
"endereco": "Avenida Principal, 456"
}
Resposta (201 Created):
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"nome": "Maria Santos",
"email": "maria.santos@email.com",
"telefone": "(11) 91234-5678",
"dataNascimento": "1985-08-20T00:00:00Z",
"endereco": "Avenida Principal, 456",
"dataCriacao": "2024-01-09T10:15:00Z",
"dataAtualizacao": null
}
Resposta (400 Bad Request):
{
"errors": [
"O nome é obrigatório",
"O email deve ser válido"
]
}
5. Atualizar Pessoa
PUT /api/pessoas/{id}
Atualiza os dados de uma pessoa existente.
Parâmetros:
-
id(Guid): ID único da pessoa
Body (JSON):
{
"nome": "Maria Santos Silva",
"email": "maria.silva@email.com",
"telefone": "(11) 91234-5678",
"dataNascimento": "1985-08-20",
"endereco": "Avenida Principal, 456 - Apto 101"
}
Resposta (200 OK):
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"nome": "Maria Santos Silva",
"email": "maria.silva@email.com",
"telefone": "(11) 91234-5678",
"dataNascimento": "1985-08-20T00:00:00Z",
"endereco": "Avenida Principal, 456 - Apto 101",
"dataCriacao": "2024-01-09T10:15:00Z",
"dataAtualizacao": "2024-01-09T10:20:00Z"
}
6. Deletar Pessoa
DELETE /api/pessoas/{id}
Remove uma pessoa do sistema.
Parâmetros:
-
id(Guid): ID único da pessoa
Resposta (204 No Content): Sem corpo de resposta
Resposta (404 Not Found):
{
"message": "Pessoa com ID {id} não encontrada"
}
💻 Implementação do Serviço
O serviço PessoaService utiliza um dicionário em memória para armazenar os dados. Em uma aplicação de produção, você substituiria isso por um banco de dados (SQL Server, PostgreSQL, MongoDB, etc.).
public class PessoaService : IPessoaService
{
private readonly Dictionary<Guid, Pessoa> _pessoas = new();
private readonly object _lock = new();
public Task<Pessoa> CriarAsync(CriarPessoaRequest request)
{
lock (_lock)
{
var pessoa = new Pessoa
{
Id = Guid.NewGuid(),
Nome = request.Nome,
Email = request.Email,
Telefone = request.Telefone,
DataNascimento = request.DataNascimento,
Endereco = request.Endereco,
DataCriacao = DateTime.UtcNow
};
_pessoas[pessoa.Id] = pessoa;
return Task.FromResult(pessoa);
}
}
// ... outros métodos
}
✅ Validação de Dados
O projeto utiliza Data Annotations para validação de dados. As validações incluem:
- Required: Campos obrigatórios
- EmailAddress: Validação de formato de email
- StringLength: Limitação de tamanho de strings
- MinimumLength: Tamanho mínimo de strings
Exemplo de validação no endpoint:
var validationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(request);
if (!Validator.TryValidateObject(request, validationContext, validationResults, true))
{
return Results.BadRequest(new { errors = validationResults.Select(v => v.ErrorMessage) });
}
🧪 Testando a API
Usando cURL
Listar pessoas:
curl -X GET http://localhost:5059/api/pessoas
Criar pessoa:
curl -X POST http://localhost:5059/api/pessoas \
-H "Content-Type: application/json" \
-d '{
"nome": "João Silva",
"email": "joao.silva@email.com",
"telefone": "(11) 98765-4321",
"dataNascimento": "1990-05-15",
"endereco": "Rua Exemplo, 123"
}'
Atualizar pessoa:
curl -X PUT http://localhost:5059/api/pessoas/{id} \
-H "Content-Type: application/json" \
-d '{
"nome": "João Silva Santos",
"email": "joao.santos@email.com",
"telefone": "(11) 98765-4321",
"dataNascimento": "1990-05-15",
"endereco": "Rua Exemplo, 123"
}'
Deletar pessoa:
curl -X DELETE http://localhost:5059/api/pessoas/{id}
Usando Postman ou Insomnia
Importe a especificação OpenAPI disponível em /swagger/v1/swagger.json no Postman ou Insomnia para ter acesso a todos os endpoints pré-configurados.
🎨 Documentação com OpenAPI e Redoc
OpenAPI/Swagger
O projeto utiliza Swashbuckle.AspNetCore para gerar automaticamente a documentação OpenAPI 3.0. A especificação OpenAPI está disponível em:
- JSON: http://localhost:5059/swagger/v1/swagger.json
- Swagger UI: http://localhost:5059/swagger (apenas em Development)
Gerando HTML Estático com Redocly CLI para Produção
Para ambiente de produção, é recomendado gerar uma versão estática da documentação usando o Redocly CLI. Isso oferece melhor performance e pode ser servido por qualquer servidor web estático.
Passo A — Instalar Redocly CLI (Node)
Primeiro, instale o Redocly CLI globalmente usando npm:
npm install -g @redocly/cli
Nota: É necessário ter o Node.js instalado em sua máquina. Você pode baixá-lo em nodejs.org.
Passo B — Gerar o OpenAPI JSON localmente
Com a API rodando, baixe o arquivo JSON da especificação OpenAPI:
curl http://localhost:5059/swagger/v1/swagger.json -o openapi.json
Ou, no Windows PowerShell:
Invoke-WebRequest -Uri http://localhost:5059/swagger/v1/swagger.json -OutFile openapi.json
Passo C — Gerar o HTML do Redoc
Execute o comando para gerar o HTML estático:
redocly build-docs openapi.json
Isso cria um arquivo redoc-static.html no diretório atual.
Passo D — Colocar o HTML no wwwroot
Agora você pode mover o arquivo gerado para o diretório wwwroot e servi-lo diretamente:
mkdir -p wwwroot
mv redoc-static.html wwwroot/index.html
Ou, no Windows PowerShell:
New-Item -ItemType Directory -Force -Path wwwroot
Move-Item -Path redoc-static.html -Destination wwwroot/index.html
Agora abra http://localhost:5059/index.html para ver a documentação estática.
Vantagens do Redocly CLI
- ✅ Configuração personalizada: Diferentes opções de configuração e temas
- ✅ Lint e validação: Valida a especificação OpenAPI antes de gerar
- ✅ Bundle da spec: Consolida referências externas em um único arquivo
- ✅ Builds automatizados: Integra facilmente em pipelines de CI/CD
- ✅ Deploy estático: Pode ser servido por qualquer servidor web estático (Nginx, Azure Static Web Apps, GitHub Pages, etc.)
- ✅ Melhor performance: HTML estático carrega mais rápido que versões dinâmicas
Exemplo de Configuração Avançada
Você também pode criar um arquivo de configuração redocly.yaml para personalizar a geração:
apis:
main:
root: openapi.json
theme:
colors:
primary:
main: '#32329f'
typography:
fontSize: '14px'
E então usar:
redocly build-docs openapi.json --config redocly.yaml
🔒 Próximos Passos e Melhorias
Para tornar esta API pronta para produção, considere:
- Persistência de Dados: Substituir o dicionário em memória por um banco de dados (Entity Framework Core, Dapper, etc.)
- Autenticação e Autorização: Implementar JWT, OAuth 2.0 ou Identity
- Logging: Adicionar logging estruturado (Serilog, NLog)
- Tratamento de Erros Global: Middleware para tratamento centralizado de exceções
- Rate Limiting: Limitar requisições por IP/usuário
- Caching: Implementar cache para consultas frequentes
- Versionamento de API: Suporte para múltiplas versões da API
- Testes Automatizados: Unit tests e integration tests
- CI/CD: Pipeline de build e deploy automatizado
- Monitoramento: Application Insights, Prometheus, etc.
📖 Conclusão
Este projeto demonstra como criar uma API RESTful completa e moderna usando .NET 8 com Minimal APIs. Através do uso de OpenAPI e Redoc, conseguimos gerar documentação automática e interativa, facilitando o desenvolvimento e a integração com outros sistemas.
As Minimal APIs do .NET 8 oferecem uma abordagem simplificada e performática para criar APIs REST, enquanto o OpenAPI e Redoc garantem uma experiência de documentação profissional e acessível.
🤝 Conecte-se Comigo
Se você trabalha com .NET moderno e quer dominar arquitetura, C#, DevOps ou interoperabilidade, vamos conversar:
💼 LinkedIn
💻 DEV Community
✍️ Medium
📰 Substack
📧 Email
⁷ Agora, pois, seja o temor do Senhor convosco; guardai-o, e fazei-o; porque não há no Senhor nosso Deus iniquidade nem acepção de pessoas, nem aceitação de suborno. 2 Crônicas 19:7
Top comments (0)