Skip to content

第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 附加结构化错误信息

练习题 ​

  1. 为 CRUD 服务的每个场景返回合适的 gRPC 状态码。
  2. 用 WithDetails 附加字段级别的校验错误。
  3. Client 端区分 NotFound 和 InvalidArgument 错误。

把零散知识,整理成可以反复走的路。