在go的網絡編程中,不管是request中的body,仍是response中的body都是io.ReadCloser類型,意味着一旦所有讀取完成,就沒法進行第二次讀取,由於在io.ReadCloser的內部會有一個標記,記錄讀取到什麼位置,所以一旦讀到尾,就不能再從頭讀取了。web
因爲ReadCloser不能Seek,所以通常的解決方法就是把body讀出來以後,再從新包裝成io.ReadColser,而後再綁定回body上。以gin中的中間件讀取爲例,先在中間件中讀取body,而後在控制器裏讀取body,sql
func printBody(ctx *gin.Context) {
defer ctx.Request.Body.Close()
body, _ := ioutil.ReadAll(ctx.Request.Body)
ctx.Request.Body = ioutil.NopCloser(bytes.NewReader(body)) //關鍵一步,把已經讀取出來的body數據,使用NopCloser從新包裝成io.ReadCloser
fmt.Println(string(body))
}
func main() {
r := gin.Default()
r.Use(printBody)
r.POST("/test", func(ctx *gin.Context) {
defer ctx.Request.Body.Close()
body, _ := ioutil.ReadAll(ctx.Request.Body)
fmt.Println("router:", string(body))
})
r.Run(":9999")
}
複製代碼
運行結果: 數據庫
若是把關鍵的那步代碼去掉,則讀取不到,變成了如下結果: 固然,若是不須要二次讀取body數據,那麼也就不必這麼操做了。以前一直用的是mongo,後來在一次開發中使用postgresql,在建表時,設置了兩個字段名,appKey和appSecret,而後在go中拼接sql語句時,一開始是這麼寫的編程
db.Where("appKey = ? AND appSecret = ?", appKey,appSecret).First(&user)
複製代碼
程序會報 pq: column "appkey" does not exist 錯誤,查看執行的sql的語句:json
SELECT * FROM "secrets" WHERE "secrets"."deletedAt" IS NULL AND ((appKey = '123' AND appSecret = '123'))
複製代碼
查閱資料以後發現,sql語句對大小寫是不敏感的,它會把appKey和appSecret當作appkey,appsecret,所以就會出錯。api
解決的方法是對須要大寫的字符串用雙引號引發來,如如下代碼網絡
db.Where("\"appKey\" = ? AND \"appSecret\" = ?", appKey,appSecret).First(&user) // 使用 \ 對雙引號進行轉義
複製代碼
查看執行的sql語句:app
SELECT * FROM "secrets" WHERE "secrets"."deletedAt" IS NULL AND (("appKey" = '123' AND "appSecret" = '123'))
複製代碼
這個時候,大小寫就被區分開了,程序也就正常了。框架
可是這樣會在代碼裏出現過多的反斜杆,不是特別的舒服,所以建議使用下劃線來從新命名字段,即appKey,appSecret改成app_key,app_secret。post
在go中,若是發送了一個http請求,返回一個json,而後要對這個json進行操做,可使用json.Unmarshal(),來將獲取到的json轉爲map,而後再進行處理。
func main() {
url := "http://localhost:9900/test"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
var data map[string]interface{}
json.Unmarshal(body, &data) // 使用Unmarshal,將返回的json轉爲map
fmt.Println(data) // map[a:1 b:2]
}
複製代碼
若是一個接口要返回一個json數據響應,則可使用json.Marshal(),來將map轉爲json字符串
func main() {
http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
data := map[string]int{
"a": 1,
"b": 4,
}
resp, _ := json.Marshal(data) // 此時的resp是[]byte,使用string(resp),能夠獲得{"a":1,"b":4}的json字符串
writer.Header().Add("Content-Type", "application/json")
writer.WriteHeader(200)
writer.Write(resp)
})
http.ListenAndServe(":6666", nil)
}
複製代碼
這兩種互相轉化的方式,在一些web框架中,被大量的使用,好比在gin中,經常使用的context.JSON()方法,最終調用的是一個WriteJSON方法,而這個方法也是很簡潔
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, jsonContentType)
jsonBytes, err := json.Marshal(obj) // 就是這裏,將傳入的interface{}轉爲[]byte形式的json字符串
if err != nil {
return err
}
_, err = w.Write(jsonBytes)
return err
}
複製代碼
使用關係型數據庫,設計表的時候,有時會設計外鍵,這個時候也會給外鍵添加約束,也就是參照完整性約束。然而在使用gorm,編寫數據模型時,發現沒法創建外鍵約束,外鍵的關聯是創建起來了,可是約束沒有創建起來。gorm使用ForeignKey,AssociationForeignKey都沒法自動創建約束,即使使用sql tag也創建不了,嘗試了好幾回。最終使用了gorm的api,用api創建外鍵約束
Client.CreateTable(&model.User{})
Client.CreateTable(&model.Profile{})
Client.Model(&model.User{}).AddForeignKey("profileId", "profile(id)", "SET NULL", "CASCADE")
// 爲user表添加profileId外鍵,並設置外鍵約束,刪除時SET NULL, 更新時CASCADE
複製代碼
注意這個AddForeignKey方法,若是已經創建了約束,再次執行程序會報錯,因此要作適當的判斷。
第一次使用go開發系統,多多少少在使用上有些不習慣,以上是在開發中遇到的一些問題,再接再礪!
Thanks!