Проверить поле на основе значения другого поля с помощью validator.v2 - PullRequest
0 голосов
/ 13 июля 2020

У меня есть следующая (упрощенная) структура:

type newAppRegister struct {
    SomeFlag       *bool    `json:"someflag" validate:"nonnil"`
    ComputeLevel   string   `json:"compute-level" validate:"computelevelvalidator"`
}

И computelevelvalidator - это некоторая функция проверки.

Я хочу, чтобы, если SomeFlag имеет значение false, то ComputeLevel будет требуется и запустить его функцию проверки.

Возможное решение - создать переменную stati c и установить ее в пользовательской функции проверки SomeFlag, например:

var someFlag bool

func someFlagValidator(v interface{}, param string) error {
    st := reflect.ValueOf(v)
    if st.Kind() != reflect.Bool {
        return fmt.Errorf("must be bool")
    }
    someFlag = st.Bool()
    return nil
}

И затем в computelevelvalidator можно установить условие на основе его значения.

Но поскольку это REST API, и он может получать много вызовов в секунду, я не уверен, что использую эту stati c переменная будет работать (меня беспокоят условия гонки - но я не совсем уверен).

Я использую go1.11 и пакет проверки gopkg.in/validator.v2 (версия gopkg.in/validator.v2 v2.0.0-20190827175613-1a84e0480e5b).

1 Ответ

1 голос
/ 13 июля 2020

Итак, сначала: Да, использование этой переменной c (глобальной) будет проблемой для одновременного использования / доступа. Это не способ go.

Если ваш лог c проверки является контекстным (как вы сказали: если флаг ложный, то определенные ограничения применяются к другому полю), тогда реализация простого Validate функция довольно проста. Я знаю, что используемый вами пакет поддерживает этот тип вещей, но внешний пакет почти всегда будет иметь довольно общий характер c. По крайней мере, вы в конечном итоге выполните утверждения типа. Последнее, что я проверил, вам все еще нужен явный вызов для проверки validator.Validate(), так почему бы не переместить его в метод вашего типа?

type newAppRegister struct {} // your type

func (n newAppRegister) Validate() error {
    if err := validator.Validate(n); err != nil {
        return err
    }
    // at this point, we now the flag field isn't nit, because it passed validation
    if !*n.SomeFlag {
        // validate ComputeLevel here
    }
    return nil
}

Нет необходимости в утверждениях типа, не говоря уже о отражении. Я бы даже сказал, что с этими двумя полями нет необходимости в пакете валидатора. Вы можете сделать то же самое с помощью стандартных тегов JSON:

type Foo struct {
    SomeFlag     *bool   `json:"some_flag,omitempty"`
    ComputeLevel string  `json:"compute_level"`
}

func (f Foo) Validate() error {
    if f.SomeFlag == nil {
        return ErrSomeFlagRequired
    }
    if !*f.SomeFlag {
        // validate ComputeLevel
    }
    return nil
}

Это довольно просто использовать:

f := Foo{}
if err := json.Unmarshal([]byte(payload), &f); err != nil {
    // some shady JSON was submitted
}
if err := f.Validate(); err != nil {
    // JSON was technically valid, but payload made no sense
}
// handle valid request
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...