第09章:错误处理与 gRPC 状态码
本章目标
掌握 gRPC 标准状态码 + 详细错误模型
9.1 gRPC 标准状态码
go
// Server 端返回错误
import "google.golang.org/grpc/codes"
import "google.golang.org/grpc/status"
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
if req.GetId() == 0 {
return nil, status.Errorf(codes.InvalidArgument, "用户ID不能为空")
}
return nil, status.Errorf(codes.NotFound, "用户 %d 不存在", req.GetId())
}| 常用状态码 | 含义 | HTTP 对应 |
|---|---|---|
OK | 成功 | 200 |
InvalidArgument | 参数错误 | 400 |
NotFound | 资源不存在 | 404 |
AlreadyExists | 资源已存在 | 409 |
PermissionDenied | 无权限 | 403 |
Unauthenticated | 未认证 | 401 |
Internal | 内部错误 | 500 |
Unavailable | 服务不可用 | 503 |
DeadlineExceeded | 超时 | 504 |
Unimplemented | 未实现 | 501 |
9.2 解析错误
go
resp, err := client.GetUser(ctx, req)
if err != nil {
st := status.Convert(err) // 或 status.FromError(err)
fmt.Printf("错误码: %v\n", st.Code()) // NotFound
fmt.Printf("错误信息: %v\n", st.Message()) // 用户 999 不存在
for _, detail := range st.Details() {
fmt.Printf("详情: %v\n", detail)
}
}9.3 详细错误(Google 模型)
go
import "google.golang.org/genproto/googleapis/rpc/errdetails"
st, _ := status.New(codes.InvalidArgument, "参数错误").WithDetails(
&errdetails.BadRequest_FieldViolation{
Field: "name",
Description: "姓名不能为空",
},
)
return st.Err()常见错误
错误1:在 Server 中同时返回 error 和 response
go
// ❌ 返回了 error,response 会被忽略
return &pb.Response{...}, status.Error(codes.Internal, "错误")
// Client 只能收到 error
// ✅ 要么返回 response + nil,要么 nil + error错误2:用 fmt.Errorf 而不是 status.Errorf
go
// ❌ fmt.Errorf 不是 gRPC 标准错误!Client 端收到的是 Unknown code
return nil, fmt.Errorf("用户不存在")
// ✅ 用 status.Errorf
return nil, status.Errorf(codes.NotFound, "用户不存在")本章小结
status.Errorf(code, msg)创建 gRPC 错误status.Convert(err)解析错误- 16 个标准错误码覆盖所有场景
- 用
WithDetails附加结构化错误信息
练习题
- 为 CRUD 服务的每个场景返回合适的 gRPC 状态码。
- 用 WithDetails 附加字段级别的校验错误。
- Client 端区分 NotFound 和 InvalidArgument 错误。