Introdução
Este tutorial demonstra como construir uma API simples para gerenciamento de receitas culinárias utilizando o framework Gin e armazenando os dados em um slice do Go.
Modelo de Dados e Endpoints da API
Modelo
type Refeicao struct {
Nome string `json:"nome"`
Tags []string `json:"tags"`
Componentes []string `json:"componentes"`
Passos []string `json:"passos"`
DataPublicacao time.Time `json:"dataPublicacao"`
}
Endpoints
| Método HTTP | Recurso | Descrição |
|---|---|---|
| GET | /refeicoes | Lista todas as reecitas |
| POST | /refeicoes | Cria uma nova receita |
| PUT | /refeicoes/:id | Atualiza uma receita existente |
| DELETE | /refeicoes/:id | Remove uma receita existente |
| GET | /refeicoes/buscar?tag=X | Busca receitas por tag |
Implementação dos Handlers
Criando uma nova receita
POST /refeicoes
package main
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/rs/xid"
)
type Refeicao struct {
ID string `json:"id"`
Nome string `json:"nome"`
Tags []string `json:"tags"`
Componentes []string `json:"componentes"`
Passos []string `json:"passos"`
DataPublicacao time.Time `json:"dataPublicacao"`
}
var listaRefeicoes []Refeicao
func init() {
listaRefeicoes = make([]Refeicao, 0)
}
func CriarRefeicaoHandler(ctx *gin.Context) {
var novaRefeicao Refeicao
if err := ctx.ShouldBindJSON(&novaRefeicao); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"erro": err.Error(),
})
return
}
novaRefeicao.ID = xid.New().String()
novaRefeicao.DataPublicacao = time.Now()
listaRefeicoes = append(listaRefeicoes, novaRefeicao)
ctx.JSON(http.StatusOK, novaRefeicao)
}
func main() {
roteador := gin.Default()
roteador.POST("/refeicoes", CriarRefeicaoHandler)
roteador.Run("127.0.0.1:8080")
}
Testando com Python
import requests
import json
def testar_criacao(dados):
url = "http://127.0.0.1:8080/refeicoes"
resposta = requests.post(url=url, data=json.dumps(dados))
print("Teste de criação:")
print(resposta.text)
if __name__ == "__main__":
dados_receita = {
"nome": "Pizza Caseira",
"tags": ["italiana", "pizza", "jantar"],
"componentes": [
"1 e 1/2 xícara (355 ml) água morna",
"1 pacote de fermento biológico seco",
"3 e 3/4 xícara de farinha de pão",
"queijo mussarela ralado",
],
"passos": [
"Passo 1.",
"Passo 2.",
"Passo 3.",
],
}
testar_criacao(dados_receita)
Listando todas as receitas
GET /refeicoes
func ListarRefeicoesHandler(ctx *gin.Context) {
ctx.JSON(http.StatusOK, listaRefeicoes)
}
func main() {
roteador.GET("/refeicoes", ListarRefeicoesHandler)
}
Atualizando uma receita existente
PUT /refeicoes/:id
func AtualizarRefeicaoHandler(ctx *gin.Context) {
id := ctx.Param("id")
var refeicaoAtualizada Refeicao
if err := ctx.ShouldBindJSON(&refeicaoAtualizada); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"erro": err.Error(),
})
return
}
indice := -1
for i := 0; i < len(listaRefeicoes); i++ {
if listaRefeicoes[i].ID == id {
indice = i
break
}
}
if indice == -1 {
ctx.JSON(http.StatusNotFound, gin.H{
"erro": "Refeição não encontrada",
})
return
}
refeicaoAtualizada.ID = id
listaRefeicoes[indice] = refeicaoAtualizada
ctx.JSON(http.StatusOK, refeicaoAtualizada)
}
func main() {
roteador.PUT("/refeicoes/:id", AtualizarRefeicaoHandler)
}
Excluindo uma receita
DELETE /refeicoes/:id
func ExcluirRefeicaoHandler(ctx *gin.Context) {
id := ctx.Param("id")
indice := -1
for i := 0; i < len(listaRefeicoes); i++ {
if listaRefeicoes[i].ID == id {
indice = i
break
}
}
if indice == -1 {
ctx.JSON(http.StatusNotFound, gin.H{
"erro": "Refeição não encontrada",
})
return
}
listaRefeicoes = append(listaRefeicoes[:indice], listaRefeicoes[indice+1:]...)
ctx.JSON(http.StatusOK, gin.H{
"mensagem": "Refeição foi excluída com sucesso",
})
}
func main() {
roteador.DELETE("/refeicoes/:id", ExcluirRefeicaoHandler)
}
Buscando receitas por tag
GET /refeicoes/buscar?tag=X
import "strings"
func BuscarRefeicaoHandler(ctx *gin.Context) {
tag := ctx.Query("tag")
resultados := make([]Refeicao, 0)
for i := 0; i < len(listaRefeicoes); i++ {
encontrada := false
for _, t := range listaRefeicoes[i].Tags {
if strings.EqualFold(t, tag) {
encontrada = true
break
}
}
if encontrada {
resultados = append(resultados, listaRefeicoes[i])
}
}
ctx.JSON(http.StatusOK, resultados)
}
func main() {
roteador.GET("/refeicoes/buscar", BuscarRefeicaoHandler)
}
Código Completo
package main
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/rs/xid"
)
type Refeicao struct {
ID string `json:"id"`
Nome string `json:"nome"`
Tags []string `json:"tags"`
Componentes []string `json:"componentes"`
Passos []string `json:"passos"`
DataPublicacao time.Time `json:"dataPublicacao"`
}
var listaRefeicoes []Refeicao
func init() {
listaRefeicoes = make([]Refeicao, 0)
}
func CriarRefeicaoHandler(ctx *gin.Context) {
var novaRefeicao Refeicao
if err := ctx.ShouldBindJSON(&novaRefeicao); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"erro": err.Error(),
})
return
}
novaRefeicao.ID = xid.New().String()
novaRefeicao.DataPublicacao = time.Now()
listaRefeicoes = append(listaRefeicoes, novaRefeicao)
ctx.JSON(http.StatusOK, novaRefeicao)
}
func ListarRefeicoesHandler(ctx *gin.Context) {
ctx.JSON(http.StatusOK, listaRefeicoes)
}
func AtualizarRefeicaoHandler(ctx *gin.Context) {
id := ctx.Param("id")
var refeicaoAtualizada Refeicao
if err := ctx.ShouldBindJSON(&refeicaoAtualizada); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"erro": err.Error(),
})
return
}
indice := -1
for i := 0; i < len(listaRefeicoes); i++ {
if listaRefeicoes[i].ID == id {
indice = i
break
}
}
if indice == -1 {
ctx.JSON(http.StatusNotFound, gin.H{
"erro": "Refeição não encontrada",
})
return
}
refeicaoAtualizada.ID = id
listaRefeicoes[indice] = refeicaoAtualizada
ctx.JSON(http.StatusOK, refeicaoAtualizada)
}
func ExcluirRefeicaoHandler(ctx *gin.Context) {
id := ctx.Param("id")
indice := -1
for i := 0; i < len(listaRefeicoes); i++ {
if listaRefeicoes[i].ID == id {
indice = i
break
}
}
if indice == -1 {
ctx.JSON(http.StatusNotFound, gin.H{
"erro": "Refeição não encontrada",
})
return
}
listaRefeicoes = append(listaRefeicoes[:indice], listaRefeicoes[indice+1:]...)
ctx.JSON(http.StatusOK, gin.H{
"mensagem": "Refeição foi excluída com sucesso",
})
}
func BuscarRefeicaoHandler(ctx *gin.Context) {
tag := ctx.Query("tag")
resultados := make([]Refeicao, 0)
for i := 0; i < len(listaRefeicoes); i++ {
encontrada := false
for _, t := range listaRefeicoes[i].Tags {
if strings.EqualFold(t, tag) {
encontrada = true
break
}
}
if encontrada {
resultados = append(resultados, listaRefeicoes[i])
}
}
ctx.JSON(http.StatusOK, resultados)
}
func main() {
roteador := gin.Default()
roteador.POST("/refeicoes", CriarRefeicaoHandler)
roteador.GET("/refeicoes", ListarRefeicoesHandler)
roteador.PUT("/refeicoes/:id", AtualizarRefeicaoHandler)
roteador.DELETE("/refeicoes/:id", ExcluirRefeicaoHandler)
roteador.GET("/refeicoes/buscar", BuscarRefeicaoHandler)
roteador.Run("127.0.0.1:8080")
}