所以我有以下内容,这似乎令人难以置信,我一直在想,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))
}
一定有更好的方法,对吧?我只是很难找到最佳实践。
(Go 在搜索引擎中也被称为 Golang,在这里提到是为了让其他人可以找到它。)
curl -X POST -H 'Content-Type: application/json' -d "{\"test\": \"that\"}"
,则 req.Form["test"]
应返回 "that"
请使用 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)
}
您需要从 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))
}
req.ParseForm()
(我在之前尝试解决此问题时所做的),在您尝试阅读 req.Body
之前,它似乎清除了正文,当您转到 {4 时抛出 unexpected end of JSON input
(至少在 1.0.2 中)
json.NewDecoder(req.Body)
的答案也是正确的。
json.Decoder
比 json.Unmarshal
更受欢迎的原因有两个 - 2013 年最流行的答案中没有解决这些原因:
2018 年 2 月,go 1.10 引入了一个新方法 json.Decoder.DisallowUnknownFields(),它解决了检测不需要的 JSON 输入 req.Body 已经是一个 io.Reader 的问题。读取其全部内容然后执行 json.Unmarshal 会浪费资源,如果流是 10MB 的无效 JSON 块。如果遇到无效的 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
More()
并没有真正按照您的预期去做:您的示例将很乐意接受 {}]
作为有效的 JSON
我被这个确切的问题逼疯了。我的 JSON Marshaller 和 Unmarshaller 没有填充我的 Go 结构。然后我在 https://eager.io/blog/go-and-json 找到了解决方案:
“与 Go 中的所有结构一样,重要的是要记住只有首字母大写的字段对 JSON Marshaller 等外部程序可见。”
在那之后,我的 Marshaller 和 Unmarshaller 完美地工作了!
Marshal/Unmarshal
JSON 无关。
我发现文档中的以下示例非常有用(来源 here)。
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)
}
}
这里的关键是 OP 正在寻找解码
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)
}
}
更新:我还要补充一点,this post 还提供了一些关于使用 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”而不是“SomeKey”(您将在内部使用)解析 JSON。
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)
}
defer req.Body.Close()
从文档中:“服务器将关闭请求正文。ServeHTTP 处理程序不需要。”还要从文档中回答@thisisnotabus:“对于服务器请求,请求正文始终为非零,但在没有正文时将立即返回 EOF”golang.org/pkg/net/http/#Requestjson.Decoder
。它适用于 JSON 对象流,而不是单个对象。对于单个 JSON 对象,它的效率并不高,因为它将整个对象读入内存。它有一个缺点,如果在对象之后包含垃圾,它不会抱怨。根据一些因素,json.Decoder
可能无法完全读取正文,并且连接将没有资格重复使用。