Gin 實現(xiàn)統(tǒng)一異常處理和封裝統(tǒng)一返回結果
在使用Gin開發(fā)web應用的時候,業(yè)務異常是很常見的,通常我們會為每個異常情況定義一個唯一的error。同時當發(fā)生異常的時候,我們也需要把異常信息放入到接口的響應信息里面,方便頁面上做提示。
//業(yè)務異常
package bizerr
const (
// 定義可預見的異常
UserNotFound = 10001
PasswrodErr = 10002
)
var resultCodeText = map[int]string{
UserNotFound: "用戶不存在",
}
func Message(code int) (string, bool) {
message, ok := resultCodeText[code]
return message, ok
}
錯誤碼這里有5位
1 | 01 | 01 |
錯誤級別,如服務級 | 模塊級,如用戶模塊 | 具體的錯誤碼,如用戶名錯誤 |
- 錯誤級別:服務級錯誤用1,普通錯誤用2,通常是用戶的非法操作
- 模塊級錯誤碼:2 位數(shù)進行表示,比如 01 為用戶模塊;02 為訂單模塊
- 具體錯誤碼:2 位數(shù)進行表示,比如 01 為手機號不合法;02 為密碼輸入錯誤
為了讓這些錯誤信息以及正常情況的返回數(shù)據(jù)都有統(tǒng)一的結構來管理,我們需要先定義一個統(tǒng)一返回的數(shù)據(jù)結構體。
要想實現(xiàn)統(tǒng)一的異常處理,我們需要借助Gin提供的中間件功能去在返回數(shù)據(jù)之前,攔截到出現(xiàn)的錯誤,在這里重新包裝成我們定義的統(tǒng)一結構體。
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Result 表示統(tǒng)一響應的JSON格式
type Result struct {
Code int `json:"code"` // 狀態(tài)碼
Message string `json:"message"` // 響應消息
Data interface{} `json:"data"` // 響應數(shù)據(jù)
}
接下來我們要給 Result 提供幾個常用的方法,比如出現(xiàn)異常時候需要調用的方法,正常情況下需要調用的方法。
func Fail(c *gin.Context, code int, message string) {
c.JSON(code, Result{
Code: code,
Message: message,
Data: nil,
})
c.Abort()
}
//異常信息從定義好的bizerr里面獲取
func Fail(c *gin.Context, code int) {
message, _ := bizerr.StatusText(code)
c.JSON(code, Result{
Code: code,
Message: message,
Data: nil,
})
c.Abort()
}
//ok 不需要返回數(shù)據(jù) data
func Ok(c *gin.Context, code int) {
c.JSON(code, Result{
Code: code,
Message: message,
Data: nil,
})
}
//接口執(zhí)行正常 需要返回數(shù)據(jù) data
func Ok(c *gin.Context, code int, message string,
data interface{} ) {
c.JSON(code, Result{
Code: code,
Message: message,
Data: data,
})
}
//接口執(zhí)行正常 需要返回數(shù)據(jù) data
func Ok(c *gin.Context, code int,
data interface{} )
{
c.JSON(code, Result{
Code: code,
Message: "ok",
Data: data,
})
}
實現(xiàn)攔截返回結果的中間件
func GlobalErrorMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
//先執(zhí)行請求
c.Next()
// 發(fā)生了錯誤
if len(c.Errors) > 0 {
//獲取最后一個error 返回
err := c.Errors.Last()
Fail(c, http.StatusInternalServerError, err.Error())
return
}
}
}
使用中間件
func main() {
r := gin.New()
r.Use(middleware.GlobalErrorMiddleware())
r.GET("/test2", func(c *gin.Context) {
m := map[string]interface{}{
"lang": "go",
}
data:=Result{
Data: m,
}
middleawre.Ok(http.Status.OK,data)
})
r.Run(":8080")
}
使用postman 返回
圖片
由此,我們看到了Gin提供的中間件的威力,中間件可以幫助我們做很多中間的事情。
通過定義統(tǒng)一的返回結構,使得我們的所有接口都可以以相同的數(shù)據(jù)結構展示給需要調用接口的人。大大提高了代碼的可讀性和維護性。