在Go中处理JSON Post Request

所以我有以下,这似乎令人难以置信的hack,我一直在想,Go有更好的设计库比这,但我找不到一个Go处理JSON数据的POST请求的例子。它们都是来自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))
}

肯定有更好的办法,对吧?我只是不知道最好的做法是什么。

(围棋在搜索引擎中也被称为Golang,这里提到它是为了让其他人可以找到它。)

341533 次浏览

你需要从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))
}

请使用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)
}

我发现下面的文档示例非常有用(来源在这里)。

package main


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


func main() {
const jsonStream = `
{"Name": "Ed", "Text": "Knock knock."}
{"Name": "Sam", "Text": "Who's 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"`
}

...现在你的处理程序将使用“some-key”来解析JSON,而不是“SomeKey”(你将在内部使用)。

这个问题快把我逼疯了。我的JSON编组器和解组器没有填充我的Go结构。然后我在https://eager.io/blog/go-and-json找到了解决方案:

与Go中的所有结构体一样,重要的是只记住这一点 首字母大写的字段对外部程序可见 比如JSON编组器。" < / p >

在那之后,我的Marshaller和Unmarshaller工作得很完美!

有两个原因,为什么json.Decoder应该比json.Unmarshal更受欢迎——这在2013年最受欢迎的答案中没有提到:

  1. 2018年2月,go 1.10引入了一个新方法json.Decoder.DisallowUnknownFields (),该方法解决了检测不需要的json输入的问题
  2. req.Body已经是一个io.Reader。如果流是一个10MB的无效JSON块,则读取其全部内容然后执行json.Unmarshal会浪费资源。如果遇到无效的JSON,使用json.Decoder解析请求体,就像它的一样,会触发早期解析错误。实时处理I/O流是首选的发展

处理一些关于检测错误用户输入的用户评论:

要执行强制字段和其他卫生检查,请尝试:

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\"}g4rB4g3@#$%^&*" 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
type test struct {
Test string `json:"test"`
}


func test(w http.ResponseWriter, req *http.Request) {
var t test_struct


body, _ := ioutil.ReadAll(req.Body)
json.Unmarshal(body, &t)


fmt.Println(t)
}

我喜欢在本地定义自定义结构。所以:

// my handler func
func addImage(w http.ResponseWriter, r *http.Request) {


// define custom type
type Input struct {
Url        string  `json:"url"`
Name       string  `json:"name"`
Priority   int8    `json:"priority"`
}


// define a var
var input Input


// decode input or return error
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
w.WriteHeader(400)
fmt.Fprintf(w, "Decode error! please check your JSON formating.")
return
}


// print user inputs
fmt.Fprintf(w, "Inputed name: %s", input.Name)


}