在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")
}
复制代码
运行结果: 数据库
以前一直用的是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!