JSON Post Request in Go afhandelen

Dus ik heb het volgende, dat ongelooflijk gehackt lijkt, en ik heb bij mezelf gedacht dat Go beter ontworpen bibliotheken heeft dan dit, maar ik kan geen voorbeeld vinden van Go die een POST-verzoek van JSON-gegevens afhandelt. Het zijn allemaal POST’s.

Hier is een voorbeeldverzoek: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

En hier is de code, met de logs ingesloten:

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

Er moet toch een betere manier zijn? Ik ben gewoon stumped in het vinden van wat de beste praktijk zou kunnen zijn.

(Go staat bij de zoekmachines ook bekend als Golang en wordt hier vermeld zodat anderen het kunnen vinden.)


Antwoord 1, autoriteit 100%

Gebruik a.u.b. json.Decoderin plaats van 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)
}

Antwoord 2, autoriteit 22%

Je moet lezen uit req.Body. De methode ParseFormleest van de req.Bodyen analyseert deze vervolgens in standaard HTTP-gecodeerd formaat. Wat u wilt, is de hoofdtekst lezen en ontleden in JSON-indeling.

Hier is uw code bijgewerkt.

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

Antwoord 3, autoriteit 16%

Ik maakte mezelf gek met dit exacte probleem. Mijn JSON Marshaller en Unmarshaller vulden mijn Go-struct niet. Toen vond ik de oplossing op https://eager.io/blog/go-and-json:

“Zoals met alle structs in Go, is het alleen belangrijk om dat te onthouden
velden met een hoofdletter zijn zichtbaar voor externe programma’s
zoals de JSON Marshaller.”

Daarna werkten mijn Marshaller en Unmarshaller perfect!


Antwoord 4, autoriteit 16%

Er zijn twee redenen waarom json.Decoderde voorkeur verdient boven json.Unmarshal– die niet aan bod komen in het meest populaire antwoord uit 2013:

  1. Februari 2018, go 1.10introduceerde een nieuwe methode json .Decoder.DisallowUnknownFields()die de bezorgdheid wegneemt van het detecteren van ongewenste JSON-invoer
  2. req.Bodyis al een io.Reader. Het lezen van de volledige inhoud en het uitvoeren van json.Unmarshalverspilt bronnen als de stream bijvoorbeeld een blok van 10 MB ongeldige JSON was. Het ontleden van de aanvraagtekst, met json.Decoder, terwijl het stroomtbinnen zou een vroege parseerfout veroorzaken als ongeldige JSON werd aangetroffen. Het verwerken van I/O-stromen in realtime heeft de voorkeur go-way.

het aanpakken van enkele van de opmerkingen van de gebruiker over het detecteren van slechte gebruikersinvoer:

Om verplichte velden af ​​te dwingen, en andere sanitaire controles, probeer dan:

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

Typische 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

Antwoord 5, Autoriteit 5%

Ik vond het volgende voorbeeld van de Docs echt behulpzaam (bron hier ).

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

De sleutel hier is dat het OP was om te decoderen

type test_struct struct {
    Test string
}

… In welk geval zouden we de const jsonStreamvallen en de Messagestruct gebruiken met de 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)
    }
}

Bijwerken : Ik zou ook toevoegen dat deze na biedt een aantal goede gegevens over het reageren met JSON ook. De auteur legt struct tags, die ik niet bewust van was.

Sinds JSON niet normaal uitzien als {"Test": "test", "SomeKey": "SomeVal"}, maar {"test": "test", "somekey": "some value"}, kunt u uw structuur herstructureren als volgt uit:

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

… en nu handler zal ontleden JSON met behulp van “sommige-key” in tegenstelling tot “SomeKey” (die u intern zal worden gebruikt).


Antwoord 6

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

Antwoord 7

Ik vind het leuk om lokaal aangepaste structuren te definiëren. Dus:

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

Other episodes