проблема с нулевым значением в моделях в grpc-go - PullRequest
3 голосов
/ 13 марта 2019

У меня есть два сервера

  1. Пользовательский сервер : обрабатывать все операции пользователя CRUD
  2. Сервер продукта : обрабатыватьРабота с продуктом CRUD и получение информации о пользователе с пользовательского сервера с помощью вызова gRPC

    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;
}

Теперь сгенерированная модель из прото-файла выглядит следующим образом: "

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"`
}

Теперь проблема в том, что я использую указатель для необязательного поля, он будет хранить ноль в случае отсутствия значения, но на противоположном сайте gRPC не поймет значение ноль и выдаст ошибку.

Я пробовал google.protobuf.StringValue значение как тип grpc, как это

google.protobuf.StringValue lastSignedInAt = 8;

Это работает, но проблема в том, что я должен написать условие для каждого поля в обработчике:

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

Каков наилучший способ решения этой проблемы? Должен ли я изменитьмодель базы данных или какие-либо изменения в модели gRPC?

Ответы [ 3 ]

1 голос
/ 18 марта 2019

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)
1 голос
/ 20 марта 2019

Как уже отмечалось в другом ответе, если в сообщении буфера протокола есть поля, которые могут быть равны нулю, вы должны проверить их. Вы ничего не можете с этим поделать, если только не существует пакета утилит, который это делает.

Если вы хотите, чтобы значения по умолчанию превышали генерируемые буферами протокола по умолчанию, вам нужно будет сделать именно то, что вы отметили, и проверить на ноль.

У меня есть несколько вопросов:

  • Зачем использовать указатели для «необязательных» полей в первой структуре пользователя? Если вы используете обычную старую строку, они будут заполнены пустой строкой при создании и не будут затронуты вызовом json unmarshal, если поле отсутствует. То же самое для полей времени. В этом случае значение по умолчанию для строки (пустая строка) должно быть недопустимым отчеством, а значение по умолчанию для времени (0001-01-01 00:00:00 +0000 UTC) является недопустимой отметкой времени (вероятно?). Избавление от указателей позволяет использовать значения по умолчанию.
  • Для метки времени в структуре proto вы все равно можете использовать строку и проверить наличие пустой строки. Или вы можете использовать google.protobuf.Timestamp и использовать ptypes для обработки преобразований в / из непротопной структуры. См. https://godoc.org/github.com/golang/protobuf/ptypes#TimestampProto. В этом случае вам придется проверить на ноль.
1 голос
/ 13 марта 2019

Вы не можете установить нулевое значение вместо себя, вы можете использовать

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

, когда вы используете одно из них, вам нужно установить только одно значение, либо вы можете установить example1 или example2, которые вы не можете использовать оба одновременно.Это решит вашу проблему по сравнению с установкой значения nil.

Подход 2:

И по умолчанию у gRPC все переменные имеют начальное значение ex: string: ""

Одна вещь, которую вы также можете сделать, не устанавливать проверку нулевого значения с условием, если ваше значение равно нулю, тогда ничего не устанавливать.

...