错误处理
Phanes 对错误进行了一个简单的封装,使得在整个系统中处理错误变的方便又简单,且使用灵活
自定义错误
使用Phanes自定义错误,只需要定义错误常量,这里你可以定义任何你自定义的错误。并且在任何地方使用。
// common Error type
const (
None Type = 0
Unauthorized Type = 1000
ErrParamsParse Type = iota + 1000
)
const (
BadRequest Type = iota + 2000
Forbidden
NotFound
InternalServerError
NotImplemented
ServiceUnavailable
)
func (t Type) String() string {
switch t {
case None:
return "none"
case BadRequest:
return "bad request"
case Unauthorized:
return "unauthorized"
case Forbidden:
return "forbidden"
case NotFound:
return "not found"
case InternalServerError:
return "internal server errors"
case NotImplemented:
return "not implemented"
case ServiceUnavailable:
return "service unavailable"
default:
return ""
}
}
这样设计的目的是为了增加错误处理的灵活性,如果一些错误是可以固定某些错误信息,那么可以直接定义在 Type 的 默认错误提示信息里面。如果有些错误可能会自己添加一些特别说明,就可以再在New的时候加入。
错误消息显示
- 如果使用时添加自定义错误信息, 如:BadRequest.New(“password is required”)
- Type 设置了默认消息提示,则会显示:
bad request: password is required
- 如果Tpye内没有设置默认消息,此时错误信息就会显示:
password is required
- Type 设置了默认消息提示,则会显示:
- 如果使用时不填写自定义错误信息,如:BadRequest.New("")
- Type 设置了默认消息提示,则会显示:
bad request
- 如果方法内没有设置默认消息,此时错误信息就会显示为空。
- Type 设置了默认消息提示,则会显示:
- 使用Wrap和Wrapf时遵循此规则
使用说明
使用New创建一个错误
import "phanes/errors"
// New 时增加自定义错误信息
if err := c.ShouldBindJSON(&u); err != nil {
c.Error(errors.ErrParamsParse.New("register unmarshal json error"))
return
}
// New 时不增加自定义错误信息
if err := c.ShouldBindJSON(&u); err != nil {
c.Error(errors.ErrParamsParse.New(""))
return
}
包装一个错误
import "phanes/errors"
// Wrap 时增加自定义错误信息
if err := c.ShouldBindJSON(&u); err != nil {
c.Error(errors.ErrParamsParse.Wrap(err, "register unmarshal json error"))
return
}
// Wrap 时不增加自定义错误信息
if err := c.ShouldBindJSON(&u); err != nil {
c.Error(errors.ErrParamsParse.Wrap(err, ""))
return
}
当你包装一个错误时,也可以像New一样,不填写后面的错误信息,规则和New一样
断言
import "phanes/errors"
var err error
// 可以通过 Is 方法判断是否是我们自定义的错误或者其他错误类型
if errors.Is(err, errors.CustomError) {
// 如果是自定义错误可以更具错误做出对应处理
// do something
}
// 如果确定此错误就是我们自定义的错误,也可以直接使用 GetType 来获取我们定义的错误类型
// 根据错误类型进行对应处理
errType := errors.GetType(err)
if errType == 1000 {
c.JSON(http.StatusUnauthorized, nil)
} else if errType > 1000 && errType < 2000 {
c.JSON(http.StatusOK, gin.H{
"trace_id": traceID,
"code": errType,
"message": e.Error(),
})
}
对外可见性(暴露错误)
由于错误的类型不同,错误的对外可见性也不一样,并且,有时候有些错误是需要经过处理之后才可以暴露出去的(比如数据库的错误是需要经过处理才能暴露的),那么我们就需要自己有一个错误处理的方案,这里我们可以灵活的去控制一个错误是否需要暴露给外部。具体代码在 server/web/middleware/util.go
当前的可见性规则是:如果不是我们 errors 包中定义的错误就会判定为不可见错误
具体判定具体代码
if errType == errors.None {
// 当错误类型为 None 时说明不是我们的错误类型,即内部错误,我们可以选择不暴露此类错误
// check request params
if errs, ok := e.Err.(validator.ValidationErrors); ok {
c.JSON(http.StatusBadRequest, gin.H{
"trace_id": traceID,
"code": errType,
"msg": RemoveTopStruct(errs.Translate(translate)),
})
} else {
// some can't show error
c.JSON(http.StatusInternalServerError, gin.H{
"trace_id": traceID,
"code": 500,
"msg": "Server Internal Error",
})
}
// 如果错误类型是我们自定义的,我们也可以选择是否暴露此错误
} else if errType == 1000 {
c.JSON(http.StatusUnauthorized, nil)
// hello Common errors handle
} else if errType > 1000 && errType < 2000 {
c.JSON(http.StatusOK, gin.H{
"trace_id": traceID,
"code": errType,
"msg": e.Error(),
})
// customer error handle here
} else if errType >= 2000 && errType < 3000 {
c.JSON(http.StatusOK, gin.H{
"trace_id": traceID,
"code": errType,
"msg": e.Error(),
})
}
你可以简单的使用我们的规则,定义新的错误类型,并在业务代码中进行错误处理来决定是否暴露,如数据库查时为空,们先定义一个新的错误类型 errors/error_type.go
RecordNotFound Type = 3000
然后在 bll 中使用此错误
if err = a.iUser.Find(in.UserId); err != nil && err = gorm.RecordNotFound {
// 此错误信息将暴露给外部,但不会给出数据库的具体错误信息,这个错误是你自己在 errors 包中定义的错误信息
return errors.RecordNotFound.New("data not found")
}else {
// 这个错误将显示 InternalServerError
return err
}
得益于我们的整体架构设计,使得我们的错误处理更加优雅