Como Criar uma API REST com Golang e Gin - Parte 3: Validação e Middleware
Na Parte 2 criamos o CRUD completo. Agora vamos adicionar validação, middleware e tratamento de erros.
Validação com Struct Tags
O Gin usa a biblioteca validator para validar dados. Adicione tags nas structs:
type Book struct {
ID string `json:"id" binding:"required"`
Title string `json:"title" binding:"required,min=3,max=100"`
Author string `json:"author" binding:"required"`
Price float64 `json:"price" binding:"required,gt=0"`
}
Agora o ShouldBindJSON valida automaticamente:
func createBook(c *gin.Context) {
var newBook Book
if err := c.ShouldBindJSON(&newBook); err != nil {
c.JSON(400, gin.H{
"error": "Dados inválidos",
"details": err.Error(),
})
return
}
books = append(books, newBook)
c.JSON(201, newBook)
}
Criando Middleware
Middleware de Logging
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// Processa a requisição
c.Next()
// Após a requisição
latency := time.Since(start)
gin.DefaultWriter.Write([]byte(fmt.Sprintf(
"[%s] %s %s %v\n",
time.Now().Format("2006-01-02 15:04:05"),
c.Request.Method,
c.Request.URL.Path,
latency,
)))
}
}
Middleware de Autenticação (API Key)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
apiKey := c.GetHeader("X-API-Key")
if apiKey != "meu-secret-key" {
c.AbortWithStatusJSON(401, gin.H{
"error": "Não autorizado",
})
return
}
c.Next()
}
}
Aplicando os Middleware
func main() {
r := gin.Default()
// Middleware global
r.Use(LoggerMiddleware())
// Grupo de rotas protegidas
books := r.Group("/books")
books.Use(AuthMiddleware())
{
books.GET("", getBooks)
books.GET("/:id", getBook)
books.POST("", createBook)
books.PUT("/:id", updateBook)
books.DELETE("/:id", deleteBook)
}
r.Run(":8080")
}
Tratamento de Erros Estruturado
Crie um helper para erros:
type AppError struct {
Message string `json:"message"`
Code int `json:"code"`
}
func NewAppError(message string, code int) *AppError {
return &AppError{Message: message, Code: code}
}
func (e *AppError) Error() string {
return e.Message
}
Uso nos handlers:
func getBook(c *gin.Context) {
id := c.Param("id")
for _, book := range books {
if book.ID == id {
c.JSON(200, book)
return
}
}
c.JSON(404, AppError{Message: "Livro não encontrado", Code: 404})
}
Testando
# Listar livros (precisa do header X-API-Key)
curl -H "X-API-Key: meu-secret-key" http://localhost:8080/books
# Criar livro com dados inválidos
curl -X POST http://localhost:8080/books \
-H "X-API-Key: meu-secret-key" \
-H "Content-Type: application/json" \
-d '{"title":"AB","price":-10}'
# Resposta de erro:
# {"error":"Dados inválidos","details":"Key: 'Book.Title' Error:Field validation for 'Title' failed on the 'min' tag..."}
Próximos Passos
Na Parte 4 (final), vamos:
- Conectar ao PostgreSQL com GORM
- Implementar migrations
- Deploy com Docker
👉 Parte 4: Banco de Dados com GORM
Gostou deste tutorial? Compartilhe no Twitter e deixe seu feedback no Telegram!