当前位置: 首页 > 知识库问答 >
问题:

grpc go模型中存在空值问题

金泉
2023-03-14

我有两台服务器

>

  • 用户服务器:处理所有用户CRUD操作
  • 产品服务器:通过gRPC调用处理产品CRUD操作并从用户服务器获取用户信息

    type User struct {
        ID string `json:"id"`
        FirstName      string     `json:"firstName"`
        MiddleName     *string    `json:"middleName,omitempty"`
        LastName       string     `json:"lastName"`
        Email          string     `json:"email"`
        Disabled       bool       `json:"disabled"`
        LastSignedInAt *time.Time `json:"lastSignedInAt,omitempty"`
        Bio            *string    `json:"bio,omitempty"`
        BirthDate      *time.Time `json:"birthDate,omitempty"`
    }
    

    这里的一些字段是可选的,因为我使用的是cockroachDB(扩展postgreSQL),所以我将它们作为指针保存,以便轻松扫描变量形式的查询结果。

    这是我的原始文件:

    message User {
        int64 id = 1;
        string firstName = 2;
        string lastName = 3;
        string email = 5;
        bool disabled = 6;
        string lastSignedInAt = 8;
        string bio = 9;
        string birthdate = 10;
        string profession = 14;
    }
    

    现在从上面的proto文件生成的模型是这样的:”

    type User struct {
        Id                   int64                 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
        FirstName            string                `protobuf:"bytes,2,opt,name=firstName,proto3" json:"firstName,omitempty"`
        LastName             string                `protobuf:"bytes,3,opt,name=lastName,proto3" json:"lastName,omitempty"`
        Email                string                `protobuf:"bytes,4,opt,name=email,json=email,proto3" json:"email,omitempty"`
        Disabled             bool                  `protobuf:"varint,6,opt,name=disabled,proto3" json:"disabled,omitempty"`
        LastSignedInAt       string                `protobuf:"bytes,8,opt,name=lastSignedInAt,proto3" json:"lastSignedInAt,omitempty"`
        Bio                  string                `protobuf:"bytes,9,opt,name=bio,proto3" json:"bio,omitempty"`
        Birthdate            string                `protobuf:"bytes,10,opt,name=birthdate,proto3" json:"birthdate,omitempty"`
    }
    

    现在的问题是,当我为可选字段使用指针时,它将在没有值的情况下存储null,但在相反的站点上,gRPC不会理解空值并抛出错误。

    我试过谷歌。protobuf。StringValue作为grpc类型的值,如下所示

    google.protobuf.StringValue lastSignedInAt = 8;
    

    这可以工作,但问题是我必须为处理程序中的每个字段编写条件:

    if lastSignedInAt != nil {
        user.LastSignedInAt = &wrappers.StringValue{Value:*lastSignedInAt}
    }
    

    解决这个问题的最佳方法是什么?我是否应该更改数据库模型或gRPC模型中的任何更改?

  • 共有3个答案

    艾原
    2023-03-14

    如果坚持使用指针,建议grpc不要使用点域

    一种方法是使用反射来转换它

    func ToGrpcData(in, out interface{}) error {
        inVal := reflect.ValueOf(in)
        if inVal.Kind() == reflect.Ptr {
            inVal = inVal.Elem()
        }
        inTyp := inVal.Type()
    
        outVal := reflect.ValueOf(out)
        if outVal.Kind() != reflect.Ptr {
            return errors.New("out data must be point value")
        }
    
        outVal = outVal.Elem()
        outTyp := outVal.Type()
    
        strWrapperType := reflect.TypeOf(wrappers.StringValue{})
        // range all 'in' fields
        for i := 0; i < inVal.NumField(); i++ {
            fTyp := inTyp.Field(i)
            fVal := inVal.Field(i)
            if fTyp.Type.Kind() == reflect.Ptr {
                switch fTyp.Type.Elem().Kind() {
                case reflect.String: // only implement string in this test
                    oFTyp, ok := outTyp.FieldByName(fTyp.Name)
                    if ok == false {
                        return errors.New("not match field in out value")
                    }
                    if oFTyp.Type.Elem() != strWrapperType {
                        return errors.New("not match field in out value")
                    }
                    if fVal.IsNil() == false {
                        outVal.FieldByName(fTyp.Name).Set(
                            reflect.ValueOf(&wrappers.StringValue{
                                Value: fVal.Elem().String(),
                            }),
                        )
                    }
                }
            } else {
                outVal.FieldByName(fTyp.Name).Set(fVal)
            }
        }
        return nil
    }
    

    用法

    type User struct {
        Name  string
        Value *string
    }
    type ServerUser struct {
        Name  string
        Value *wrappers.StringValue
    }
    usValue := "123"
    u1 := User{
        Name:  "tom",
        Value: &usValue,
    }
    u2 := ServerUser{}
    err := ToGrpcData(&u1, &u2)
    // error process ...
    fmt.Println(u2)
    
    韩智敏
    2023-03-14

    您不能设置空值而不是您可以使用

    oneof Examples {
        Example1 example1 = 1;
        Example2 example2 = 2;
    }
    

    当您使用其中一个时,您必须只设置一个值,您可以设置示例1或示例2,但不能同时使用这两个值。与设置nil值相比,这将解决您的问题。

    方法2:

    默认情况下,gRPC使所有变量都具有初始值,例如:string:“”

    你也可以做的一件事不要设置nil值检查条件,如果你的值为nil,那么什么也不设置。

    诸葛利
    2023-03-14

    正如上面提到的另一个答案,如果协议缓冲区消息中的字段可以为零,那么您需要检查它们。对此你无能为力,除非有一个实用程序包可以做到这一点。

    如果您希望默认值高于和超过协议缓冲区生成的默认值,则必须完全按照您注意到的那样执行并检查是否为nil。

    不过,我确实有一些问题:

    • 为什么在first User结构中为“可选”字段使用指针?如果您使用普通旧字符串,它们将在构造时填充空字符串,并且如果字段丢失,则不会被json散集调用触及。时间字段也是如此。在这种情况下,字符串的默认值(空字符串)应该是无效的中间名,而时间的默认值(0001-01-01 00:00:00 0000UTC)是无效的时间戳(可能?)。摆脱指针允许您使用默认值。
    • 对于原始结构中的时间戳,您仍然可以使用字符串并检查空字符串。或者您可以使用google.protobuf.时间戳并使用ptype来处理与非原始结构的转换。请参阅https://godoc.org/github.com/golang/protobuf/ptypes#TimestampProto.在这种情况下,您必须检查是否为零。
     类似资料:
    • 问题内容: 使用的技术:MySQL 5.1和PHP 5.3 我正在为我正在编写的网站设计一个新的数据库。我正在寻找现在存储纬度和经度值的最佳方法。 过去,我一直使用DECIMAL并使用PHP / MySQL选择以下形式: 查找最近的匹配地点。 开始阅读有关新技术的更多信息,我想知道是否应该使用Spatial Extensions。http://dev.mysql.com/doc/refman/5.

    • 我的控制器中有一个断点,并且传递的模型中的所有值都为空。你知道为什么会这样吗?

    • 问题内容: 我正在sklearn中使用模型来规范模型的功能。 现在,我想使用相同的缩放器来标准化测试集: 但我不想一直将训练数据与结合使用。有没有一种方法可以保存缩放器并稍后从其他文件中加载它? 问题答案: 所以我实际上不是这方面的专家,但是通过一些研究和一些有用的链接,我认为并将成为您的朋友。 该软件包使您可以将模型或“转储”模型保存到文件中。 我认为此链接也有帮助。它讨论了创建持久性模型。您将

    • 我是JPA和Spring的初学者。这也是我的第一个问题。所以,为我的错误道歉。我正在练习以简单的场景作为开始。我有两个实体作为产品和类别,具有双向多对一/一对多关联。 类别类: 产品类别: CategoryRepository和ProductRepository都是从JpaRepository实现的。很少有带有@Query注释的方法签名。 使用@autowmed存储库的服务。没有任何商业逻辑。 类

    • 问题内容: 是否可以在Scrapy管道中访问django模型,以便将已抓取的数据直接保存到模型中? 我已经看到了,但是我真的不知道如何设置它吗? 问题答案: 如果还有其他人遇到相同的问题,这就是我解决的方法。 我将其添加到我的scrapy settings.py文件中: 注意:上面的路径是到你的django项目文件夹,而不是settings.py文件。 现在,你将可以在scrapy项目中完全访问d

    • 我有一个问题,为我的路线创建一个驱动程序。由于某些原因,它不识别属性。 这是我的路线代码: 这是我的控制器的代码: 当我使用传统函数时也会发生同样的情况,这些函数不是箭头函数。 另一方面,这是我的express配置,其中配置的路由为: 我得到这个错误: E:\elArchivero\api\控制器\index.js: 6res.send("sended");^ 类型错误:无法读取对象处未定义的属性