gin 定义统一处理错误
在 gin 中如果有错误需要响应给客户端,如果每一个都判断,并且处理返回,如果项目复杂了,需要写很多重复的代码来响应错误,今天我们来封装一个统一处理错误包装器,使用的是装饰器模式。
1. 定义统一处理错误
pkg/e/error.go
package e
import "github.com/gin-gonic/gin"
// 自定义 api 错误结构体
type ApiError struct {
Status int `json:"-"`
Code int `json:"code"`
Message string `json:"message"`
}
func (err ApiError)Error() string {return err.Message}
2. 编写统一错误处理函数
pkg/e/error_wrapper.go
package e
import (
"github.com/gin-gonic/gin"
"net/http"
)
type WrapperHandle func(c *gin.Context) (interface{}, error)
func ErrorWrapper(handle WrapperHandle) gin.HandlerFunc {return func(c *gin.Context) {data, err := handle(c)
if err != nil {apiError := err.(ApiError)
c.JSON(apiError.Status, apiError)
return
}
c.JSON(http.StatusOK, gin.H{"data": data})
}
}
这里的统一处理只是一个简单的处理,介绍一种思路,具体的可以根据项目进行改造
3. 例子
- 编写控制器
api/err_handle.go
package api
import (
"cn.sockstack/gin_demo/pkg/e"
"github.com/gin-gonic/gin"
"net/http"
)
func ErrorHandle(c *gin.Context) (interface{}, error) {query := c.Query("q")
if query == "" {
return nil, e.ApiError{
Status: http.StatusOK, // 状态码
Code: 404,
Message: "q 的参数不能为空",
}
}
if query == "test" {
return nil, e.ApiError{
Status: http.StatusOK, // 状态码
Code: 404,
Message: "q 的参数不能为 test",
}
}
return query, nil
}
- 注册路由
routers/test.go
package routers
import (
"cn.sockstack/gin_demo/api"
"cn.sockstack/gin_demo/pkg/e"
"github.com/gin-gonic/gin"
)
func test(r *gin.Engine) {
// 定义 /test 路由
r.GET("/test", api.Test)
// 响应 json 数据
r.GET("/json", api.Json)
// 响应 xml 数据
r.GET("/xml", api.Xml)
// 统一错误处理测试
r.GET("/error", e.ErrorWrapper(api.ErrorHandle))
}
- 运行并访问 127.0.0.1:8081/error?q=hello
{"data": "hello"}
正常响应结果
- 运行并访问 127.0.0.1:8081/error?q=test
{
"code": 404,
"message": "q 的参数不能为 test"
}
- 运行并访问 127.0.0.1:8081/error
{
"code": 404,
"message": "q 的参数不能为空"
}
可以看到,两次访问都能处理错误结果,并以 json 返回,在业务开发中,只要返回自定义的 ApiError 错误,即可以统一格式返回错误。
这一节介绍的统一错误处理的灵感来源于 gin 的中间件。