Go语言中JSON Post请求的处理

unguejic  于 2023-03-20  发布在  Go
关注(0)|答案(8)|浏览(232)

所以我有了下面的代码,看起来非常的老套,我一直在想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,在这里提到它是为了让其他人能够找到它。)

rks48beu

rks48beu1#

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

2w3rbyxf2#

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

mzaanser3#

json.Decoder优于json.Unmarshal的原因有两个--2013年最流行的答案中没有提到这两个原因:

  1. 2018年2月,go 1.10引入了一种新方法json.Decoder.DisallowUnknownFields(),该方法解决了检测不需要的JSON输入的问题
  2. req.Body已经是一个io.Reader。如果流是(比如说10 MB的无效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)

Playground
典型输出:

$ 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
polhcujo

polhcujo4#

这个问题让我快把自己逼疯了,我的JSON编组器和解组器没有填充Go结构体,然后我在https://eager.io/blog/go-and-json找到了解决方案:
与Go语言中的所有结构体一样,需要记住的是,只有首字母大写的字段才能被外部程序(如JSON Marshaller)看到。
从那以后,我的编组器和解组器工作得非常完美!

hsvhsicv

hsvhsicv5#

我发现下面的例子从文档真的很有帮助(源代码在这里)。

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

更新:我还要补充一点,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。

velaa5lx

velaa5lx6#

我喜欢在本地定义自定义结构体。

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

}
nhaq1z21

nhaq1z217#

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

zazmityj8#

早期的ReadAll函数是ioutil包的一部分,但是现在它被弃用了,但是现在io包本身有了ReadAll函数。

type test struct {
  Test string `json:"test"`
}

func test(w http.ResponseWriter, req *http.Request) {
  var t test_struct
  body, _ := io.ReadAll(req.Body)
  json.Unmarshal(body, &t)
  fmt.Println(t)
}

相关问题