---
# System prepended metadata

title: 04. Gin framwork (demo with todolist)
tags: [golang練習]

---

# 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")
		}
	}
}


```