web-dev-qa-db-ja.com

GoでのJSON Post要求の処理

だから私は信じられないほどハックに思える次のものがあり、Goはこれよりもライブラリを設計した方が良いと考えてきましたが、JSONのPOSTリクエストを処理するGoの例を見つけることができませんデータ。それらはすべてフォーム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としても知られ、他の人が見つけられるようにここで言及されています。)

218
TomJ

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

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

私はこの正確な問題に夢中になっていました。私のJSON MarshallerとUnmarshallerは私のGo構造体を生成しませんでした。それから私は https://eager.io/blog/go-and-json で解決策を見つけました:

「Goのすべての構造体と同様に、頭文字が大文字のフィールドだけがJSON Marshallerのような外部プログラムに表示されることを忘れないでください。」

その後、私の元帥と元帥は完璧に働きました!

40
Steve Stilson

私はドキュメントから次の例がとても役に立つことを見つけました(source 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)
    }
}

更新この記事 は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"`
}

...そして今、あなたのハンドラは "SomeKey"(これは内部的に使用されます)ではなく "some-key"を使用してJSONを解析します。

16
JohnnyCoder

json.Decoderjson.Unmarshalよりも優先されるべきである理由は2つあります - 2013年の最も一般的な答えでは扱われていません。

  1. 2018年2月、go 1.10は新しいメソッドを導入しました json.Decoder.DisallowUnknownFields() このメソッドは不要なJSON入力を検出する懸念に対処します
  2. req.Bodyはすでにio.Readerです。コンテンツ全体を読み取ってからjson.Unmarshalを実行すると、ストリームが無効なJSONの10MBブロックであるとしたら、リソースが無駄になります。無効なJSONが検出された場合、json.Decoderストリームしながらリクエスト本文を解析すると、早期解析エラーが発生します。リアルタイムでI/Oストリームを処理することが好ましいgo-wayです。

不適切なユーザー入力の検出に関するユーザーのコメントに対処する

必須項目やその他の衛生チェックを実施するには、次のことを試してください。

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
12
colminator