Обработка запроса JSON в Google

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

Вот пример запроса: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

И вот код с встроенными в него журналами:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type test_struct struct {
    Test string
}

func test(rw http.ResponseWriter, req *http.Request) {
    req.ParseForm()
    log.Println(req.Form)
    //LOG: map[{"test": "that"}:[]]
    var t test_struct
    for key, _ := range req.Form {
        log.Println(key)
        //LOG: {"test": "that"}
        err := json.Unmarshal([]byte(key), &t)
        if err != nil {
            log.Println(err.Error())
        }
    }
    log.Println(t.Test)
    //LOG: that
}

func main() {
    http.HandleFunc("/test", test)
    log.Fatal(http.ListenAndServe(":8082", nil))
}

Там должен быть лучший способ, не так ли? Я просто смущен в поиске лучшей практики.

(Go также известен как Golang для поисковых систем и упоминается здесь, чтобы другие могли его найти.)

Ответ 1

Пожалуйста, используйте json.Decoder вместо json.Unmarshal.

func test(rw http.ResponseWriter, req *http.Request) {
    decoder := json.NewDecoder(req.Body)
    var t test_struct
    err := decoder.Decode(&t)
    if err != nil {
        panic(err)
    }
    log.Println(t.Test)
}

Ответ 2

Вам нужно прочитать req.Body. Метод ParseForm читает из req.Body и затем анализирует его в стандартном кодированном формате HTTP. Вы хотите прочитать тело и проанализировать его в формате JSON.

Здесь ваш код обновлен.

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "io/ioutil"
)

type test_struct struct {
    Test string
}

func test(rw http.ResponseWriter, req *http.Request) {
    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        panic(err)
    }
    log.Println(string(body))
    var t test_struct
    err = json.Unmarshal(body, &t)
    if err != nil {
        panic(err)
    }
    log.Println(t.Test)
}

func main() {
    http.HandleFunc("/test", test)
    log.Fatal(http.ListenAndServe(":8082", nil))
}

Ответ 3

Я сводил себя с ума с этой точной проблемой. Мой JSON Marshaller и Unmarshaller не заполняли мою структуру Go. Затем я нашел решение по адресу https://eager.io/blog/go-and-json:

"Как и во всех структурах Go, важно помнить, что только поля с заглавной первой буквой видны внешним программам, таким как JSON Marshaller".

После этого мои маршаллеры и унмаршаллеры сработали отлично!

Ответ 4

Я нашел следующий пример из документации действительно полезным (источник здесь).

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "log"
    "strings"
)

func main() {
    const jsonStream = '
        {"Name": "Ed", "Text": "Knock knock."}
        {"Name": "Sam", "Text": "Who there?"}
        {"Name": "Ed", "Text": "Go fmt."}
        {"Name": "Sam", "Text": "Go fmt who?"}
        {"Name": "Ed", "Text": "Go fmt yourself!"}
    '
    type Message struct {
        Name, Text string
    }
    dec := json.NewDecoder(strings.NewReader(jsonStream))
    for {
        var m Message
        if err := dec.Decode(&m); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%s: %s\n", m.Name, m.Text)
    }
}

Ключевым моментом здесь является то, что ОП пытался декодировать

type test_struct struct {
    Test string
}

... в этом случае мы отбрасываем const jsonStream и заменяем структуру Message на test_struct:

func test(rw http.ResponseWriter, req *http.Request) {
    dec := json.NewDecoder(req.Body)
    for {
        var t test_struct
        if err := dec.Decode(&t); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }
        log.Printf("%s\n", t.Test)
    }
}

Обновление: я бы также добавил, что в этом посте представлены отличные данные об ответах с помощью JSON. Автор объясняет struct tags, о которых я не знал.

Поскольку JSON обычно не выглядит как {"Test": "test", "SomeKey": "SomeVal"}, а скорее {"test": "test", "somekey": "some value"}, вы можете реструктурировать свой структура как это:

type test_struct struct {
    Test string 'json:"test"'
    SomeKey string 'json:"some-key"'
}

... и теперь ваш обработчик будет анализировать JSON, используя "some-key" вместо "SomeKey" (который вы будете использовать внутри).

Ответ 5

Есть две причины, по которым json.Decoder должен быть предпочтительнее json.Unmarshal - это не рассматривается в самом популярном ответе 2013 года:

  1. Февраль 2018, go 1.10 представил новый метод json.Decoder.DisallowUnknownFields(), который решает проблему обнаружения нежелательного JSON-ввода
  2. req.Body уже io.Reader. Чтение всего содержимого и последующее выполнение json.Unmarshal тратит ресурсы, если поток был, скажем, 10-мегабайтным блоком недопустимого JSON. Синтаксический анализ тела запроса с помощью json.Decoder вызовет ошибку раннего разбора, если будет обнаружен недопустимый JSON. Обработка потоков ввода/вывода в реальном времени является предпочтительным способом.

Обращаясь к некоторым комментариям пользователей об обнаружении неверного ввода пользователя:

Чтобы ввести обязательные поля и другие санитарные проверки, попробуйте:

d := json.NewDecoder(req.Body)
d.DisallowUnknownFields() // catch unwanted fields

// anonymous struct type: handy for one-time use
t := struct {
    Test *string 'json:"test"' // pointer so we can test for field absence
}{}

err := d.Decode(&t)
if err != nil {
    // bad JSON or unrecognized json field
    http.Error(rw, err.Error(), http.StatusBadRequest)
    return
}

if t.Test == nil {
    http.Error(rw, "missing field 'test' from JSON object", http.StatusBadRequest)
    return
}

// optional extra check
if d.More() {
    http.Error(rw, "extraneous data after JSON object", http.StatusBadRequest)
    return
}

// got the input we expected: no more, no less
log.Println(*t.Test)

Детская площадка

Типичный вывод:

$ curl -X POST -d "{}" http://localhost:8082/strict_test

expected json field 'test'

$ curl -X POST -d "{\"Test\":\"maybe?\",\"Unwanted\":\"1\"}" http://localhost:8082/strict_test

json: unknown field "Unwanted"

$ curl -X POST -d "{\"Test\":\"oops\"}[email protected]#$%^&*" http://localhost:8082/strict_test

extraneous data after JSON

$ curl -X POST -d "{\"Test\":\"Works\"}" http://localhost:8082/strict_test 

log: 2019/03/07 16:03:13 Works