[gin]基于切片实现crud

前言

代码参考自《Building Distributed Application in Gin》
需求:设计一个食谱相关的API,数据存放到切片中。

设计模型和API

模型

type Recipe struct {
	// 菜品名
	Name         string    `json:"name"`
	// 菜品标签,字符串切片
	Tags         []string  `json:"tags"`
	// 菜品材料
	Ingredients  []string  `json:"ingredients"`
	// 制作步骤
	Instructions []string  `json:"instructions"`
	// 发布时间
	PublishedAt  time.Time `json:"publishedAt"`
}

API

http method resource description
GET /recipes 返回一列recipe数据
POST /recipes 创建新食谱
PUT /recipes/{id} 更新一个已存在的食谱
DELETE /recipes/{id} 删除一个已存在的食谱
GET /recipes/search?tag=X 根据标签查询食谱

实现接口

添加一个新食谱

  • POST /recipes
package main

import (
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/rs/xid"
)

type Recipe struct {
	ID           string    `json:"id"`
	Name         string    `json:"name"`
	Tags         []string  `json:"tags"`
	Ingredients  []string  `json:"ingredients"`
	Instructions []string  `json:"instructions"`
	PublishedAt  time.Time `json:"publishedAt"`
}

// 保存recipes数据
var recipes []Recipe

func init() {
	recipes = make([]Recipe, 0)
}

func NewRecipeHandler(c *gin.Context) {
	// POST /recipes。创建新食谱
	var recipe Recipe
	if err := c.ShouldBindJSON(&recipe); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	recipe.ID = xid.New().String()
	recipe.PublishedAt = time.Now()
	recipes = append(recipes, recipe)
	c.JSON(http.StatusOK, recipe)
}

func main(){
	router := gin.Default()
    router.POST("/recipes", NewRecipeHandler)
    router.Run("127.0.0.1:8080")
}
  • 使用python进行测试
import requests
import json


def post_test(data):
    url = "http://127.0.0.1:8080/recipes"
    resp = requests.post(url=url, data=json.dumps(data))
    print("post test")
    print(resp.text)

if __name__ == "__main__":
    data1 = {
        "name": "Homemade Pizza",
        "tags": ["italian", "pizza", "dinner"],
        "ingredients": [
            "1 1/2 cups (355 ml) warm water (105°F-115°F)",
            "1 package (2 1/4 teaspoons) of active dry yeast",
            "3 3/4 cups (490 g) bread flour",
            "feta cheese, firm mozzarella cheese, grated",
        ],
        "instructions": [
            "Step 1.",
            "Step 2.",
            "Step 3.",
        ],
    }
    post_test(data1)

获取所有食谱

  • GET /recipes
func ListRecipesHandler(c *gin.Context) {
	c.JSON(http.StatusOK, recipes)
}

func main(){
	router.GET("/recipes", ListRecipesHandler)
}
  • python测试
def get_test():
    url = "http://127.0.0.1:8080/recipes"
    resp = requests.get(url)
    print("get test")
    print(resp.text)

get_test()

根据食谱id更新食谱

func UpdateRecipeHandler(c *gin.Context) {
	// PUT /recipes/:id
	id := c.Param("id")
	var recipe Recipe
	if err := c.ShouldBindJSON(&recipe); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	index := -1
	// 根据id遍历查询
	for i := 0; i < len(recipes); i++ {
		if recipes[i].ID == id {
			index = i
		}
	}

	if index == -1 {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Recipe Not Found",
		})
		return
	}
	
	recipe.ID = id
	recipes[index] = recipe
	c.JSON(http.StatusOK, recipe)
}

func main(){
	router.PUT("/recipes/:id", UpdateRecipeHandler)
}
  • python测试
def put_test(data, id):
    url = f"http://127.0.0.1:8080/recipes/{id}"
    # data["id"] = id
    resp = requests.put(url=url, data=json.dumps(data))
    print("put test")
    print(resp.text)

data2 = {
	"name": "西红柿炒鸡蛋",
	"tags": ["家常菜", "新手必会"],
	"ingredients": [
		"2个鸡蛋",
		"1个番茄, 切片",
		"葱花, 蒜瓣等",
	],
	"instructions": [
		"步骤1",
		"步骤2",
		"步骤3",
	],
}

put_test(data2, id="someidstring")

根据id删除食谱

func DeleteRecipeHandler(c *gin.Context) {
	// DELETE /recipes/:id
	id := c.Param("id")
	index := -1
	// 根据id遍历查询
	for i := 0; i < len(recipes); i++ {
		if recipes[i].ID == id {
			index = i
		}
	}

	if index == -1 {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Recipe Not Found",
		})
		return
	}

	// 通过切片切割实现删除元素
	recipes = append(recipes[:index], recipes[index+1:]...)
	
	c.JSON(http.StatusOK, gin.H{
		"message": "Recipe has been deleted",
	})
}

func main(){
	router.DELETE("/recipes/:id", DeleteRecipeHandler)
}
  • python测试
def delete_test(id):
    url = f"http://127.0.0.1:8080/recipes/{id}"
    resp = requests.delete(url=url)
    print("delete test")
    print(resp.text)

delete_test(id="someIdString")

根据标签搜索食谱

func SearchRecipeHandler(c *gin.Context) {
	// GET /recipes/search
	tag := c.Query("tag")
	listOfRecipes := make([]Recipe, 0)

	for i := 0; i < len(recipes); i++ {
		found := false
		for _, t := range recipes[i].Tags {
			if strings.EqualFold(t, tag) {
				found = true
			}
		}

		if found {
			listOfRecipes = append(listOfRecipes, recipes[i])
		}
	}

	c.JSON(http.StatusOK, listOfRecipes)
}

func main(){
	router.GET("/recipes/search", SearchRecipeHandler)
}
  • python测试
def get_test_search(tag):
    url = f"http://127.0.0.1:8080/recipes/search?tag={tag}"
    resp = requests.get(url=url)
    print("get test search")
    print(resp.text)

get_test_search(tag="家常菜")

完整示例代码

package main

import (
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/rs/xid"
)

type Recipe struct {
	ID           string    `json:"id"`
	Name         string    `json:"name"`
	Tags         []string  `json:"tags"`
	Ingredients  []string  `json:"ingredients"`
	Instructions []string  `json:"instructions"`
	PublishedAt  time.Time `json:"publishedAt"`
}

// 保存recipes
var recipes []Recipe

func init() {
	recipes = make([]Recipe, 0)
}

func NewRecipeHandler(c *gin.Context) {
	// POST /recipes。创建新食谱
	var recipe Recipe
	if err := c.ShouldBindJSON(&recipe); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	recipe.ID = xid.New().String()
	recipe.PublishedAt = time.Now()
	recipes = append(recipes, recipe)
	c.JSON(http.StatusOK, recipe)
}

func ListRecipesHandler(c *gin.Context) {
	c.JSON(http.StatusOK, recipes)
}

func UpdateRecipeHandler(c *gin.Context) {
	// PUT /recipes/:id
	id := c.Param("id")
	var recipe Recipe
	if err := c.ShouldBindJSON(&recipe); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	index := -1
	for i := 0; i < len(recipes); i++ {
		if recipes[i].ID == id {
			index = i
		}
	}

	if index == -1 {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Recipe Not Found",
		})
		return
	}
	recipe.ID = id
	recipes[index] = recipe
	c.JSON(http.StatusOK, recipe)
}

func DeleteRecipeHandler(c *gin.Context) {
	// DELETE /recipes/:id
	id := c.Param("id")
	index := -1
	for i := 0; i < len(recipes); i++ {
		if recipes[i].ID == id {
			index = i
		}
	}

	if index == -1 {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Recipe Not Found",
		})
		return
	}

	recipes = append(recipes[:index], recipes[index+1:]...)
	c.JSON(http.StatusOK, gin.H{
		"message": "Recipe has been deleted",
	})
}

func SearchRecipeHandler(c *gin.Context) {
	// GET /recipes/search
	tag := c.Query("tag")
	listOfRecipes := make([]Recipe, 0)

	for i := 0; i < len(recipes); i++ {
		found := false
		for _, t := range recipes[i].Tags {
			if strings.EqualFold(t, tag) {
				found = true
			}
		}

		if found {
			listOfRecipes = append(listOfRecipes, recipes[i])
		}
	}

	c.JSON(http.StatusOK, listOfRecipes)
}

func main() {
	router := gin.Default()
	router.POST("/recipes", NewRecipeHandler)
	router.GET("/recipes", ListRecipesHandler)
	router.PUT("/recipes/:id", UpdateRecipeHandler)
	router.DELETE("/recipes/:id", DeleteRecipeHandler)
	router.GET("/recipes/search", SearchRecipeHandler)
	router.Run("127.0.0.1:8080")
}

相关文章

简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议。...
使用gin框架编写服务端应用,配置路由接收websocket请求并处...
## 前言 linux自带的crontab默认情况下只能精确到分钟,没法...
前言 代码参考自《Building Distributed Application in Gin...
前言 通过钉钉群机器人的webhook,实现消息推送。 本文代码仅...
golang-jwt是go语言中用来生成和解析jwt的一个第三方库,早先...