Как выводить ошибки формата json в Golang rest, особенно ссылаясь на плохие поля - PullRequest
0 голосов
/ 30 июня 2019

У меня есть следующее требование: возвращать ошибки из API REST в следующем формате:

Error format
422
{
    "name-of-field": [
        "can't be blank",
        "is too silly"
    ]
}

Мой код выглядит следующим образом:

var PostFeedback = func(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    surveyId := params["id"]
    feedback := &models.Feedback{}
    err := json.NewDecoder(r.Body).Decode(feedback)
    if err != nil {
        jsonError := fmt.Sprintf(`{
            "%s": [
                "%s"
            ]
        }`, "errors", err)
        log.Printf("invalid input format, %v", jsonError)
        resp := map[string]interface{}{"error": jsonError}
        u.Respond(w, resp)
        return
    }

Вопросы:

Как я могу получить имена полей, которые нарушают работу?
Как мне лучше всего удовлетворить требование?

1 Ответ

1 голос
/ 30 июня 2019

Пакет encoding/json не обеспечивает проверку ни пустых, ни глупых значений. Она будет возвращать ошибку только в том случае, если данные в теле не являются допустимым json, или если типы полей в json не соответствуют, в соответствии со спецификацией пакета, типам полей структуры, в которую Вы пытаетесь расшифровать этот JSON.

Первым типом ошибки будет json.SyntaxError, если вы получите это, не всегда возможно удовлетворить ваши требования, поскольку могут быть нет фактических полей, которые вы могли бы используйте в своем ответе, или если есть поля json, они и их значения могут быть совершенно правильными, но причина ошибки может быть в другом месте (см. пример ).

В тех случаях, когда данные содержат фактические поля json, но имеют, например, значения, отличные от json, вы можете использовать поле Offset типа SyntaxError, чтобы найти ближайшее предшествующее поле в потоке данных. Используя strings.LastIndex, вы можете реализовать наивное решение, чтобы посмотреть назад на поле.

data := []byte(`{"foobar": i'm not json}`)

err := json.Unmarshal(data, &T{})
se, ok := err.(*json.SyntaxError)
if !ok {
    panic(err)
}

field := string(data[:se.Offset])
if i := strings.LastIndex(field, `":`); i >= 0 {
    field = field[:i]
    if j := strings.LastIndex(field, `"`); j >= 0 {
        field = field[j+1:]
    }
}
fmt.Println(field) // outputs foobar

Ссылка на игровую площадку

ПРИМЕЧАНИЕ: Как видите, для того, чтобы вы могли искать поле, вам необходим доступ к данным, но когда вы используете json.NewDecoder и передаете его запросу. непосредственно, без предварительного хранения его содержимого где-нибудь, вы потеряете доступ к этим данным, как только метод декодера Decode будет завершен. Это связано с тем, что тело представляет собой поток байтов, обернутый в io.ReadCloser, который не поддерживает «перемотку», т. Е. Вы не можете перечитать байтов, которые декодер уже прочитал. Чтобы избежать этого, вы можете использовать ioutil.ReadAll, чтобы прочитать полное содержимое тела, а затем json.Unmarshal, чтобы выполнить декодирование.


Вторым типом ошибки будет json.UnmarshalTypeError. Если вы посмотрите документацию по типу ошибки и ее полям, вы поймете, что все, что вам нужно сделать, - это ввести возвращенное значение, и все готово. Пример * +1051 ** * тысяча пятьдесят две


Проверка по "пустым" и "глупым" значениям будет выполнена после того, как json будет успешно декодирован в вашу структуру. Как вы это делаете, зависит от вас. Например, вы можете использовать сторонний пакет, предназначенный для проверки структур, или вы можете самостоятельно реализовать собственное решение и т. Д. У меня нет мнения, какой из них является «лучшим», поэтому я могу ». Я не могу тебе помочь с этим.

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

...