前言
代码参考自《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") }