gin框架使用注意事項
本文就說下這段時間我在使用gin框架過程中遇到的問題和要注意的事情。
錯誤處理請求返回要使用c.Abort,不要只是return
當在controller中進行錯誤處理的時候,發現一個錯誤,往往要立即返回,這個時候要記得使用gin.Context.Abort 或者其相關的函數。
類似於:
if err != nil {
c.AbortWithStatus(500)
return
}
這個Abort函數本質是提前結束后續的handler鏈條,(通過將handler的下標索引直接變化為 math.MaxInt8 / 2 )但是前面已經執行過的handler鏈條(包括middleware等)還會繼續返回。
gin的Abort系列的幾個函數為:
func (c *Context) Abort()
func (c *Context) AbortWithStatus(code int)
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{})
func (c *Context) AbortWithError(code int, err error)
gin的錯誤處理
gin本身默認加載了Recovery()的中間件,所以在不知道如何處理error的時候,可以直接panic出去
如何獲取response的body
需求來源於我要做個gin的中間件,請求進來的時候記錄一下請求參數,請求出去的時候記錄一下請求返回值。在記錄請求返回值的時候,我就需要得到請求的返回內容。但是context里面只有一個結構:
Writer gin.ResponseWriter
所以這里基本思路就是創建一個Writer,它繼承gin.ResponseWriter。同時,它又有一個byte.buffer來copy一份數據。
// bodyLogWriter是為了記錄返回數據到log中進行了雙寫
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
所以,在middleware中就應該這么寫
sTime := time.Now()
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
// 請求結束的時候記錄
duration := fmt.Sprintf("%fms", float64(time.Now().Sub(sTime).Nanoseconds()) / 1000000.0)
handler.Tracef(c.Request.Context(), logger.DLTagRequestOut,
"proc_time=%s||response=%s",
duration,
blw.body.String())
主要就是在Next之前吧context.Writer用我們定義的Writer給替換掉,讓它輸出數據的時候寫兩份。
如何獲取所有的請求參數
這個其實和gin框架沒有啥關系,我剛開始使用的時候以為使用request.ParseForm,然后在request.Form中就能得到了。
結果發現當我的Content-type為multipart/form-data的時候,竟然解析不到數據。
追到ParseForm里面發現,http/request.go里面有這么一個部分代碼
case ct == "multipart/form-data":
// handled by ParseMultipartForm (which is calling us, or should be)
// TODO(bradfitz): there are too many possible
// orders to call too many functions here.
// Clean this up and write more tests.
// request_test.go contains the start of this,
// in TestParseMultipartFormOrder and others.
}
我的golang版本是1.11.4。當content-type為multipart/form-data的時候是空的調用的。
當然注釋也寫很清楚了,建議使用ParseMultipartForm
所以獲取http參數的函數我就寫成這個樣子:
// 這個函數只返回json化之后的數據,且不處理錯誤,錯誤就返回空字符串
func getArgs(c *gin.Context) []byte {
if c.ContentType() == "multipart/form-data" {
c.Request.ParseMultipartForm(defaultMemory)
} else {
c.Request.ParseForm()
}
args, _ := json.Marshal(c.Request.Form)
return args
}