在Go中处理JSON Post请求

Tom*_*omJ 230 json go

所以我有以下,看起来非常h​​acky,我一直在想自己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))
}
Run Code Online (Sandbox Code Playgroud)

必须有更好的方法,对吗?我很难找到最好的做法.

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

Joe*_*Joe 362

请使用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)
}
Run Code Online (Sandbox Code Playgroud)

  • 首先,看起来这可以处理流而不是需要你自己将它全部加载到缓冲区中.(我是一个不同的乔BTW) (79认同)
  • 你能解释一下原因吗? (68认同)
  • 我建议不要使用`json.Decoder`.它适用于JSON对象流,而不是单个对象.单个JSON对象的效率并不高,因为它将整个对象读入内存.它有一个缺点,如果在对象之后包含垃圾,它将不会抱怨.根据几个因素,`json.Decoder`可能无法完全读取正文,连接将无法重复使用. (16认同)
  • 我认为你不需要"推迟req.Body.Close()"来自文档:"服务器将关闭请求体.ServeHTTP处理程序不需要." 还要从文档中回答@thisisnotabus:"对于服务器请求,Request Body总是非零,但是当没有正文时会立即返回EOF"https://golang.org/pkg/net/http/#Request (14认同)
  • 我想知道在这种情况下如何正确的错误处理.我认为对无效的json恐慌不是一个好主意. (6认同)
  • 不,不要使用json.Decoder。它默默地传递了几个错误的输入(例如`{} {“ foo”:“ bar”}`,并且用于json对象流。此答案应该不被接受-这是有害的,已经引起了我的测试问题。 (3认同)
  • @skripted我相信恐慌()只是一个例子.然而,正确的错误处理将处理请求的关键/致命错误,例如无法解码json.通常会有一个围绕HTTPHandler的包装器用于中间件和相关的通用安全性等.在该中间件/包装器中,我通常处理来自HTTPHandlers的"错误"响应,作为对最终用户的通用Json错误响应(因此所有响应,甚至错误都是json编码的).这只是处理它的众多方法之一. (2认同)
  • 投反对票,因为你没有说为什么不使用 json.Unmarshal。 (2认同)

Dan*_*iel 79

你需要阅读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))
}
Run Code Online (Sandbox Code Playgroud)


小智 50

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

"与Go中的所有结构一样,重要的是要记住只有具有大写第一个字母的字段才能被JSON Marshaller等外部程序看到."

在那之后,我的Marshaller和Unmarshaller完美地工作了!

  • 我只是浪费了很多时间来弄清楚同样的事情! (4认同)
  • 请包含链接中的一些片段。如果它被弃用,示例将会丢失。 (3认同)
  • 您正式是我的英雄。我为此进行了2周的尝试,试图弄清为什么它不起作用。 (2认同)
  • 这个答案与问题和提供的链接并不真正相关,而只是关于“Marshal/Unmarshal” JSON。 (2认同)
  • 天哪,为什么…… (2认同)

col*_*tor 35

有两点原因json.Decoder值得我们优先考虑json.Unmarshal-2013年最受欢迎的答案中没有解决这些问题:

  1. 2018年2月,go 1.10引入了一种新方法json.Decoder.DisallowUnknownFields() ,该方法解决了检测不需要的JSON输入的问题
  2. req.Body已经是io.Readerjson.Unmarshal如果流是一个10MB的无效JSON块,则读取其全部内容然后执行操作会浪费资源。解析请求主体,用json.Decoder的,因为它中,如果遇到无效的JSON会引发早期解析错误。处理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)
Run Code Online (Sandbox Code Playgroud)

操场

典型输出:

$ 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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢您解释观点,而不是仅仅指出某事不好 (3认同)

Joh*_*der 19

我发现文档中的以下示例非常有用(源于此处).

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

type test_struct struct {
    Test string
}
Run Code Online (Sandbox Code Playgroud)

...在这种情况下,我们将删除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)
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:我还要补充一点,这篇文章也提供了一些关于使用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"`
}
Run Code Online (Sandbox Code Playgroud)

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


Ami*_*aei 7

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

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

}
Run Code Online (Sandbox Code Playgroud)