J'ai donc ce qui suit, qui semble incroyablement hacky, et je me suis dit que Go avait des bibliothèques mieux conçues que celle-ci, mais je ne trouve pas d'exemple où Go gère une POST demande de JSON. Les données. Ils sont tous sous forme de post.
Voici un exemple de demande: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test
Et voici le code, avec les journaux intégrés:
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))
}
Il doit y avoir un meilleur moyen, non? Je suis juste embarrassé pour trouver ce que la meilleure pratique pourrait être.
(Go est également connu sous le nom de Golang dans les moteurs de recherche et est mentionné ici pour que d'autres puissent le trouver.)
Veuillez utiliser json.Decoder
au lieu de 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)
}
Vous devez lire req.Body
. La méthode ParseForm
lit à partir de req.Body
puis l’analyse au format HTTP codé standard. Ce que vous voulez, c'est lire le corps et l'analyser au format JSON.
Voici votre code mis à jour.
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))
}
Je me rendais fou avec ce problème précis. JSON Marshaller et Unmarshaller ne peuplaient pas ma structure Go. Ensuite, j'ai trouvé la solution sur https://eager.io/blog/go-and-json :
"Comme pour toutes les structures de Go, il est important de se rappeler que seuls les champs avec une première lettre en majuscule sont visibles par des programmes externes tels que JSON Marshaller."
Après cela, mon Marshaller et Unmarshaller ont parfaitement fonctionné!
Il y a deux raisons pour lesquelles json.Decoder
devrait être préféré à json.Unmarshal
- qui ne sont pas abordées dans la réponse la plus populaire de 2013:
go 1.10
a introduit une nouvelle méthode json.Decoder.DisallowUnknownFields () qui répond au souci de détecter les entrées JSON indésirables.req.Body
est déjà un io.Reader
. Lire tout son contenu, puis exécuter json.Unmarshal
gaspille des ressources si le flux était, par exemple, un bloc de 10 Mo de JSON non valide. L'analyse du corps de la demande, avec json.Decoder
_, car il se diffuse dans déclencherait une erreur d'analyse précoce si un JSON non valide était rencontré. Le traitement des flux d'E/S en temps réel est la méthode préférée .Répondant à certains des commentaires des utilisateurs sur la détection des entrées utilisateur incorrectes:
Pour appliquer les champs obligatoires et d’autres contrôles d’assainissement, essayez:
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)
Sortie typique:
$ 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
J'ai trouvé l'exemple suivant tiré de la documentation vraiment utile (source ici ).
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)
}
}
La clé ici étant que l'OP cherchait à décoder
type test_struct struct {
Test string
}
... Dans ce cas, nous abandonnerions le const jsonStream
et remplacerons la structure Message
par le 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 : J'ajouterais également que cet article fournit également d'excellentes données sur la façon de répondre avec JSON. L'auteur explique struct tags
, que je ne connaissais pas.
Comme JSON ne ressemble normalement pas à {"Test": "test", "SomeKey": "SomeVal"}
, mais plutôt à {"test": "test", "somekey": "some value"}
, vous pouvez restructurer votre structure de la manière suivante:
type test_struct struct {
Test string `json:"test"`
SomeKey string `json:"some-key"`
}
... et maintenant votre gestionnaire analysera JSON en utilisant "une clé" par opposition à "SomeKey" (que vous utiliserez en interne).