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