# 04. Gin framwork (demo with todolist) ###### tags: `golang練習`###### tags: `golang練習` ## TL;DR: 1. DB query的時候若使用find(返回值非唯一時),記得使用limit() 1. DB 需使用時才開,不要開啟來等 1. 可使用c.Set()傳遞變數 1. 回傳訊息請統一格式 1. Url Scheme, Host, port, path, Key_type等資料請寫進config 1. load config 請不要寫死(可以使用cmd做彈性讀取) ## Set router ```go func Set_router(configs *tools.Config_data) { r := gin.New() r.Use(gin.Recovery()) r.Use(middlewares.Load_configs(configs)) // for k8s livenessprobe r.GET("/alive", alive) // funcitons r.GET("/api", get) // 首頁 r.POST("/api", create) // 建立 r.PATCH("/api", update) // 更新 r.DELETE("/api", delete) // 刪除 err := endless.ListenAndServe(":30003", r) if err != nil { log.Error().Caller().Str("func", "endless.ListenAndServe(\":30003\", r)").Err(err).Msg("Web") return } } ``` 1. **gin.New() or gin.Default()** * create a router(engine) to dierect to other path or function. * Default() include two middlewares, Logger() and Recovery(). * Logger(): write the logs during the server serving. * Recovery(): recover from any panic and send a 500 code to server if it happened. 1. **r.USE, GET, POST, PATCH, PUT, DELETE (path, func)** * request methods ask the path react by this function 1. **endless.ListenAndServe** * to make graceful shutdown(先關閉連接埠,確保沒有新的使用者連上服務,第二步驟就是確保處理完剩下的 http 連線才會正常關閉). * there is another way to do it. [here](https://chenyitian.gitbooks.io/gin-web-framework/content/docs/38.html) --- ## Create ```go // Binding from JSON type New_subject struct { Subject string `json:"subject" binding:"required,min=1"` User_id string `json:"user_id"` } func create(c *gin.Context) { var subject New_subject // 接收前端訊息 if err := c.ShouldBindJSON(&subject); err != nil { log.Error().Caller().Str("func", "ShouldBindJSON(&subject)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "subject type error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } user_id, err := tools.Parse_int(subject.User_id) if err != nil { log.Error().Caller().Str("func", "tools.Parse_int(user_id_raw").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "user_id convert error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 開DB db_conn, err := models.Db_conn_web(c) defer models.Db_close(db_conn) if err != nil { log.Error().Caller().Str("func", "tools.Db_conn(c)").Err(err).Msg("Db") if err := tools.Msg_send(c, "error", "db error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 找user var user models.Account err = db_conn.Where("Id = ? ", user_id).First(&user).Error if err != nil { log.Error().Caller().Str("func", "db_conn.Where(\"id = ?\", user_id))").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db query error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 建立資料並寫入 err = db_conn.Model(&user).Association("Todolists").Append([]models.Todolist{{Subject: subject.Subject, Status: 1}}) if err != nil { log.Error().Caller().Str("func", "db_conn.Model(&user).Association(\"Todolist\")").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db create error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 成功 if err := tools.Msg_send(c, "success", "subject created", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") return } } ``` 1. **c.ShouldBindJSON(&struct)** * authenticate the data from server match in the struct and the tag(binding: ...) --- ## Get ```go func get(c *gin.Context) { // 拿取user_id user_id_raw, b := c.GetQuery("user_id") if !b { log.Error().Caller().Str("func", "c.GetQuery(\"user_id\")").Str("msg", "get user id failed").Msg("Web") if err := tools.Msg_send(c, "error", "get user id failed", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // set user_id for other function c.Set("user_id", user_id_raw) user_id, err := tools.Parse_int(user_id_raw) if err != nil { log.Error().Caller().Str("func", "tools.Parse_int(user_id_raw)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "user_id convert error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 取得分群 group, b := c.GetQuery("group") if !b { log.Error().Caller().Str("func", "c.GetQuery(\"group\")").Str("msg", "get group failed").Msg("Web") if err := tools.Msg_send(c, "error", "get group failed", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 取得頁面顯示數 slice_target_raw, b := c.GetQuery("slice_target") if !b { log.Error().Caller().Str("func", "c.GetQuery(\"slice_target\")").Str("msg", "get slice_target failed").Msg("Web") if err := tools.Msg_send(c, "error", "get slice_target failed", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } slice_target, err := tools.Parse_int(slice_target_raw) if err != nil { log.Error().Caller().Str("func", "tools.Parse_int(slice_target_raw)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "slice_target convert error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 取得當前頁數 page_raw, b := c.GetQuery("page") if !b { log.Error().Caller().Str("func", "c.GetQuery(\"page\")").Str("msg", "get page failed").Msg("Web") if err := tools.Msg_send(c, "error", "get page failed", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } page, err := tools.Parse_int(page_raw) if err != nil { log.Error().Caller().Str("func", "tools.Parse_int(page_raw)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "page convert error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 無<1的頁面 if page <= 0 { if err := tools.Msg_send(c, "error", "no other page", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 開DB db_conn, err := models.Db_conn_web(c) defer models.Db_close(db_conn) if err != nil { log.Error().Caller().Str("func", "tools.Db_conn(c)").Err(err).Msg("Db") if err := tools.Msg_send(c, "error", "db error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } var todolists []models.Todolist // 查詢所有非0(軟刪除的)data err = db_conn.Order("id desc").Where("account_id = ? ", user_id).Scopes(tools.Db_query_group(group, 100, 0), tools.Db_query_page(page, slice_target)).Find(&todolists).Error if err != nil { log.Error().Caller().Str("func", "db_conn.Order(\"id desc\")").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db query error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 根據資料量表示是否有後續分頁 if len(todolists) > 0 { data := make(map[string]interface{}) for i, v := range todolists { si := fmt.Sprint(i) data[si] = gin.H{"id": v.ID, "status": v.Status, "subject": v.Subject} } if err := tools.Msg_send(c, "success", "list send", data); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") return } // 第一次啟動 } else if len(todolists) == 0 && page == 1 { if err := tools.Msg_send(c, "success", "no data send", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") return } // 無分頁 } else { if err := tools.Msg_send(c, "error", "no other page", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") return } } } ``` 1. **c.GetQuery(key)** * query the url data by key, return value and bool. 1. **c.Set(key, value)** * set value to used in other paths, functions.(擺脫變數的作用域) --- ## Delete ```go type Delete_msg struct { User_id string `json:"user_id"` } func delete(c *gin.Context) { var dm Delete_msg // 接收前端訊息 if err := c.ShouldBindJSON(&dm); err != nil { log.Error().Caller().Str("func", "ShouldBindJSON(&subject)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "subject type error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } user_id, err := tools.Parse_int(dm.User_id) if err != nil { log.Error().Caller().Str("func", "tools.Parse_int(user_id_raw)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "user_id convert error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 開DB db_conn, err := models.Db_conn_web(c) defer models.Db_close(db_conn) if err != nil { log.Error().Caller().Str("func", "tools.Db_conn(c)").Err(err).Msg("Db") if err := tools.Msg_send(c, "error", "db error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } var todolist []models.Todolist count := 0 // 若資料量>100, 則分批刪除 for { err := db_conn.Where("Status = ? AND account_id = ? ", 2, user_id).Limit(100).Select("ID", "Status").Find(&todolist).Error if err != nil { log.Error().Caller().Str("func", "db_conn.Where(\"Status = ?\", 2)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db query error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } break } // 判斷是否有資料,無資料則回傳"no object selected",以提示無資料經選取 if len(todolist) == 0 && count == 0 { if err := tools.Msg_send(c, "error", "no object selected", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } break // 無剩餘可刪除,刪除結束 } else if len(todolist) == 0 && count > 0 { if err := tools.Msg_send(c, "success", "all deleted", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } break // 有資料則更改status為0(軟刪除)並count++ } else { err = db_conn.Model(&todolist).Update("Status", "0").Error if err != nil { log.Error().Caller().Str("func", " db_conn.Model(&todolist)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db deleted error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } break } count++ } } } ``` --- ## update(patch, put) ```go // Binding from JSON type Update_subject struct { Id string `json:"id" binding:"required,min=1"` Subject string `json:"subject" binding:"required,min=1"` Status string `json:"status" binding:"required,min=1"` } func update(c *gin.Context) { // 接收前端訊息 var subject Update_subject if err := c.ShouldBindJSON(&subject); err != nil { log.Error().Caller().Str("func", "ShouldBindJSON(&subject)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "subject type error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // id string to int subject_id, err := strconv.Atoi(subject.Id) if err != nil { log.Error().Caller().Str("func", "strconv.Atoi(subject.Id)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "id type error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // 開DB db_conn, err := models.Db_conn_web(c) defer models.Db_close(db_conn) if err != nil { log.Error().Caller().Str("func", "tools.Db_conn(c)").Err(err).Msg("Db") if err := tools.Msg_send(c, "error", "db error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } var todolist models.Todolist // 先找出欲異動資料 err = db_conn.Where("ID = ? AND Status <> ?", uint(subject_id), 0).Take(&todolist).Error if err != nil { log.Error().Caller().Str("func", "db_conn.Where(\"ID = ? AND Status <> ?\", uint(id_int), 0)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db query error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } // update // complete則更新指定ID的status為2(完成的) if subject.Status == "complete" { // 更新 err = db_conn.Model(&todolist).Update("Status", "2").Error if err != nil { log.Error().Caller().Str("func", "db_conn.Model(&todolist).Update(\"Status\", \"2\")").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db updated error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } if err := tools.Msg_send(c, "success", "updated", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } // active則更新指定ID的status為1(未完成的) } else if subject.Status == "active" { // 更新 err = db_conn.Model(&todolist).Update("Status", "1").Error if err != nil { log.Error().Caller().Str("func", "db_conn.Model(&todolist).Update(\"Status\", \"1\")").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db updated error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } if err := tools.Msg_send(c, "success", "updated", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } // 判斷是否為更改標題 } else if subject.Status == "subject_change" { err = db_conn.Model(&todolist).Update("Subject", subject.Subject).Error if err != nil { log.Error().Caller().Str("func", "db_conn.Model(&todolist).Update(\"Subject\", subject.Subject)").Err(err).Msg("Web") if err := tools.Msg_send(c, "error", "db updated error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } return } if err := tools.Msg_send(c, "success", "updated", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } // 非以上則錯誤 } else { log.Error().Caller().Str("func", "none").Str("msg", "status unknown").Msg("Web") if err := tools.Msg_send(c, "error", "status error", nil); err != nil { log.Error().Caller().Str("func", "tools.Msg_send").Err(err).Msg("Web") } } } ```