GRPC服务端返回的错误如何被客户端正确处理?

需求服务端有个根据手机号查找用户的方法,分三种情况:找不到、能找到(返回用户信息)、出错。客户端想实现判断手机号是否已经注册,想利用这个方法。服务端代码大致如下:

func (u *UserServer) GetUserByMobile(ctx context.Context, req *userpb.MobileRequest) (*userpb.UserInfoResponse, error) {
    var user model.User
    result := global.DB.Where(&model.User{Mobile: req.Mobile}).First(&user)
    if result.RowsAffected == 0 {
        return nil, status.Errorf(codes.NotFound, "用户不存在")
    }

    if result.Error != nil {
        return nil, status.Errorf(codes.Internal, "内部错误")
    }
    userInfoRsp := ModelToResponse(user) //模型转响应用的。
    return &userInfoRsp, nil
}

客户端代码大致如下(省去表单验证等环节):

    rsp, err := userClient.GetUserByMobile(ctx, &userpb.MobileRequest{Mobile: phoneExistForm.Mobile})
    if err != nil {
        ctx.JSON(http.StatusInternalServerError, gin.H{
            "msg": "internal error",
        })
        return
    }
    if rsp.Id == 0 {
        ctx.JSON(http.StatusOK, gin.H{
            "msg": "this phone is not in use",
        })
        return
    }
    ctx.JSON(http.StatusOK, gin.H{
        "msg": "this phone is already in use",
    })

调用接口,输入一个已经被使用的手机号,提示手机号已经被使用,符合预期。但是输入一个没有被使用的手机号,提示internal error,也不知道哪个环节出了问题,求大佬解答。

最佳答案

参见文档的错误处理一章:错误处理《GORM 中文文档》

查询结果为空时result.Error是不为nil的,需要判断结果是否为空是直接判断result.Error是否为ErrRecordNotFound

你的代码可以改为如下:


func (u *UserServer) GetUserByMobile(ctx context.Context, req *userpb.MobileRequest) (*userpb.UserInfoResponse, error) {
    var user model.User
    result := global.DB.Where(&model.User{Mobile: req.Mobile}).First(&user)

    if result.Error != nil {
         if errors.Is(result.Error, ErrRecordNotFound) {
            return nil, status.Errorf(codes.NotFound, "用户不存在")
        }
        return nil, status.Errorf(codes.Internal, "内部错误")
    }

    userInfoRsp := ModelToResponse(user) //模型转响应用的。
    return &userInfoRsp, nil
}
3周前 评论
meitian (楼主) 3周前
meitian (楼主) 3周前
讨论数量: 7

常规的调用gprc服务后,返回的结构体都会断言一次吧。然后,你的报错,用的框架的话,把报错日志找出来排查就行了

3周前 评论

参见文档的错误处理一章:错误处理《GORM 中文文档》

查询结果为空时result.Error是不为nil的,需要判断结果是否为空是直接判断result.Error是否为ErrRecordNotFound

你的代码可以改为如下:


func (u *UserServer) GetUserByMobile(ctx context.Context, req *userpb.MobileRequest) (*userpb.UserInfoResponse, error) {
    var user model.User
    result := global.DB.Where(&model.User{Mobile: req.Mobile}).First(&user)

    if result.Error != nil {
         if errors.Is(result.Error, ErrRecordNotFound) {
            return nil, status.Errorf(codes.NotFound, "用户不存在")
        }
        return nil, status.Errorf(codes.Internal, "内部错误")
    }

    userInfoRsp := ModelToResponse(user) //模型转响应用的。
    return &userInfoRsp, nil
}
3周前 评论
meitian (楼主) 3周前
meitian (楼主) 3周前

@meitian 问题出在你的客户端判断没有被使用的手机号和服务端返回的内容没有关联上

两个方案:

  1. 服务端当用户不存在的时候不返回错误,而是返回id=0的user
  2. 客户端在err != nil的情况下再判断err.Error()=="用户不存在"
3周前 评论
meitian (楼主) 3周前
renxiaotu (作者) 3周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!