在 Go 中处理 JSON Post 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15672556/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 18:50:26  来源:igfitidea点击:

Handling JSON Post Request in Go

jsongo

提问by TomJ

So I have the following, which seems incredibly hacky, and I've been thinking to myself that Go has better designed libraries than this, but I can't find an example of Go handling a POST request of JSON data. They are all form POSTs.

因此,我有以下内容,这似乎令人难以置信,而且我一直在想,Go 设计的库比这更好,但我找不到 Go 处理 JSON 数据的 POST 请求的示例。它们都是表单 POST。

Here is an example request: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

这是一个示例请求: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

And here is the code, with the logs embedded:

这是代码,嵌入了日志:

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))
}

There's got to be a better way, right? I'm just stumped in finding what the best practice could be.

一定有更好的方法,对吧?我只是难以找到最佳实践。

(Go is also known as Golang to the search engines, and mentioned here so others can find it.)

(Go 也被搜索引擎称为 Golang,在此提及以便其他人可以找到它。)

回答by Joe

Please use json.Decoderinstead of json.Unmarshal.

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

回答by Daniel

You need to read from req.Body. The ParseFormmethod is reading from the req.Bodyand then parsing it in standard HTTP encoded format. What you want is to read the body and parse it in JSON format.

您需要从req.Body. 该ParseForm方法从 中读取req.Body,然后以标准 HTTP 编码格式对其进行解析。您想要的是读取正文并以 JSON 格式解析它。

Here's your code updated.

这是您的代码更新。

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))
}

回答by Steve Stilson

I was driving myself crazy with this exact problem. My JSON Marshaller and Unmarshaller were not populating my Go struct. Then I found the solution at https://eager.io/blog/go-and-json:

我被这个确切的问题逼疯了。我的 JSON Marshaller 和 Unmarshaller 没有填充我的 Go 结构。然后我在https://eager.io/blog/go-and-json找到了解决方案:

"As with all structs in Go, it's important to remember that only fields with a capital first letter are visible to external programs like the JSON Marshaller."

“与 Go 中的所有结构一样,重要的是要记住,只有首字母大写的字段对 JSON Marshaller 等外部程序可见。”

After that, my Marshaller and Unmarshaller worked perfectly!

之后,我的 Marshaller 和 Unmarshaller 完美运行!

回答by colm.anseo

There are two reasons why json.Decodershould be preferred over json.Unmarshal- that are not addressed in the most popular answer from 2013:

有两个原因为什么json.Decoder应该被优先考虑json.Unmarshal——这在 2013 年最受欢迎的答案中没有解决:

  1. February 2018, go 1.10introduced a new method json.Decoder.DisallowUnknownFields()which addresses the concern of detecting unwanted JSON-input
  2. req.Bodyis already an io.Reader. Reading its entire contents and then performing json.Unmarshalwastes resources if the stream was, say a 10MB block of invalid JSON. Parsing the request body, with json.Decoder, as it streamsin would trigger an early parse error if invalid JSON was encountered. Processing I/O streams in realtime is the preferred go-way.
  1. 2018 年 2 月,go 1.10引入了一种新方法json.Decoder.DisallowUnknownFields(),它解决了检测不需要的 JSON 输入的问题
  2. req.Body已经是io.Reader. json.Unmarshal如果流是 10MB 的无效 JSON 块,则读取其全部内容然后执行会浪费资源。如果遇到无效的 JSON json.Decoder,则在它流入时使用 解析请求正文将触发早期解析错误。实时处理 I/O 流是首选方法


Addressing some of the user comments about detecting bad user input:

解决一些关于检测不良用户输入的用户评论:

To enforce mandatory fields, and other sanitation checks, try:

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

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)

Playground

操场

Typical output:

典型输出:

$ 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

回答by JohnnyCoder

I found the following example from the docs really helpful (source here).

我发现文档中的以下示例非常有用(来源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)
    }
}

The key here being that the OP was looking to decode

这里的关键是 OP 正在寻找解码

type test_struct struct {
    Test string
}

...in which case we would drop the const jsonStream, and replace the Messagestruct with the test_struct:

...在这种情况下,我们将删除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)
    }
}

Update: I would also add that this postprovides some great data about responding with JSON as well. The author explains struct tags, which I was not aware of.

更新:我还要补充一点,这篇文章也提供了一些关于使用 JSON 响应的重要数据。作者解释了struct tags,这是我不知道的。

Since JSON does not normally look like {"Test": "test", "SomeKey": "SomeVal"}, but rather {"test": "test", "somekey": "some value"}, you can restructure your struct like this:

由于 JSON 通常看起来不像{"Test": "test", "SomeKey": "SomeVal"},而是{"test": "test", "somekey": "some value"},您可以像这样重构您的结构:

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

...and now your handler will parse JSON using "some-key" as opposed to "SomeKey" (which you will be using internally).

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

回答by Angelica Payawal

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)
}