折腾了一天,算是找到了一种解决方法

就是我现在需要在调用 rpc 时传递全局错误,我定义的全局错误如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package rpcError

// RpcError rpc 错误,参考了 https://junedayday.github.io/2021/05/07/go-tip/go-tip-3/

type Code int

const (
ErrPersonNotExistCode Code = iota + 1000
ErrPersonCreateFailedCode
ErrPersonUpdateFailedCode
ErrPersonDeleteFailedCode

ErrClassNotExistCode Code = iota + 2000
ErrClassCreateFailedCode
ErrClassUpdateFailedCode
ErrClassDeleteFailedCode
ErrClassGetListFailedCode

ErrRedisSetFailedCode Code = iota + 3000
ErrRedisGetFailedCode
)

var errCodeMap = map[Code]string{
ErrPersonNotExistCode: "用户不存在",
ErrPersonCreateFailedCode: "用户创建失败",
ErrPersonUpdateFailedCode: "用户更新失败",
ErrPersonDeleteFailedCode: "用户删除失败",

ErrClassNotExistCode: "班级不存在",
ErrClassCreateFailedCode: "班级创建失败",
ErrClassUpdateFailedCode: "班级更新失败",
ErrClassDeleteFailedCode: "班级删除失败",
ErrClassGetListFailedCode: "班级列表获取失败",

ErrRedisSetFailedCode: "redis缓存失败",
ErrRedisGetFailedCode: "redis获取失败",
}

var (
ErrPersonNotExist = NewRpcError(ErrPersonNotExistCode)
ErrPersonCreateFailed = NewRpcError(ErrPersonCreateFailedCode)
ErrPersonUpdateFailed = NewRpcError(ErrPersonUpdateFailedCode)
ErrPersonDeleteFailed = NewRpcError(ErrPersonDeleteFailedCode)

ErrClassNotExist = NewRpcError(ErrClassNotExistCode)
ErrClassCreateFailed = NewRpcError(ErrClassCreateFailedCode)
ErrClassUpdateFailed = NewRpcError(ErrClassUpdateFailedCode)
ErrClassDeleteFailed = NewRpcError(ErrClassDeleteFailedCode)
ErrClassGetListFailed = NewRpcError(ErrClassGetListFailedCode)

ErrRedisSetFailed = NewRpcError(ErrRedisSetFailedCode)
ErrRedisGetFailed = NewRpcError(ErrRedisGetFailedCode)
)

type RpcError struct {
Code Code `json:"code"`
Message string `json:"message"`
}

func NewRpcError(code Code) *RpcError {
return &RpcError{
Code: code,
Message: errCodeMap[code],
}
}

func (e *RpcError) Error() string {
return e.Message
}

但是,rpc 传递过去之后,grpc给他又封装了一层,就不能用 errors.Is() 来判断了

image-20221120121236228

image-20221120121241421

然后折腾了好久,最后经人指点可以使用 gppc 的 state 来传递错误码(

先把全局错误码改成 codes.Code 类型,然后自己写一个函数用来比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type RpcError struct {
Code codes.Code `json:"code"`
Message string `json:"message"`
}

func Is(err error, target *RpcError) bool {

if err == nil {
return false
}

s, _ := status.FromError(err)

return s.Code() == target.Code
}

然后在 rpc 层和 api 层这样使用

1
2
3
4
5
// rpc
err = l.svcCtx.DBList.Mysql.First(&classModel).Error
if err != nil {
return &class.GetPersonReply{}, status.Error(rpcError.ErrClassNotExist.Code, err.Error())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// api

// 通过学号查询用户信息
GetPersonResult, err := l.svcCtx.ClassRpc.GetPerson(l.ctx, &class.GetPersonRequest{
Id: int32(studentId),
})

if err == nil { // 如果没有错误,说明用户已经存在
//...
} else if rpcError.Is(err, rpcError.ErrPersonNotExist) { // 如果用户不存在,就创建一个
//...
} else {
return "", errors.New("get user info failed")
}

但是我还是不能理解官方文档是怎么直接比较的

image-20221120121540210