Обработка запроса 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 года:
- Февраль 2018,
go 1.10
представил новый метод json.Decoder.DisallowUnknownFields(), который решает проблему обнаружения нежелательного JSON-ввода -
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