Manejo de la solicitud JSON Post en Go


Así que tengo lo siguiente, que parece increíblemente hackeado, y he estado pensando que Go tiene bibliotecas mejor diseñadas que esto, pero no puedo encontrar un ejemplo de Go manejando una solicitud POST de datos JSON. Todos son mensajes de formulario.

Aquí hay una solicitud de ejemplo: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

Y aquí está el código, con los registros incrustados:

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

Tiene que haber una mejor manera, ¿verdad? Estoy perplejo al encontrar cuál podría ser la mejor práctica.

(Go también se conoce como Golang a los motores de búsqueda, y se menciona aquí para que otros puedan encontrarlo.)

 185
Author: TomJ, 2013-03-28

4 answers

Utilice json.Decoder en lugar 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)
}
 289
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-20 17:35:10

Necesitas leer de req.Body. El método ParseForm está leyendo desde el req.Body y luego analizándolo en formato estándar codificado HTTP. Lo que quieres es leer el cuerpo y analizarlo en formato JSON.

Aquí está tu código actualizado.

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))
}
 59
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-06 08:49:24

Me estaba volviendo loco con este problema exacto. Mi JSON Marshaller y Unmarshaller no estaban poblando mi estructura Go. Entonces encontré la solución en https://eager.io/blog/go-and-json :

"Al igual que con todas las estructuras en Go, es importante recordar que solo los campos con una primera letra mayúscula son visibles para programas externos como el JSON Marshaller."

Después de eso, mi Marshaller y mi Unmshaller funcionaron perfectamente!

 31
Author: Steve Stilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-15 00:01:24

Encontré el siguiente ejemplo de los documentos realmente útil (fuente aquí).

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 clave aquí es que el OP estaba buscando decodificar

type test_struct struct {
    Test string
}

} en cuyo caso eliminaríamos el const jsonStream, y reemplazaríamos la estructura Message con el 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)
    }
}

Actualización : También añadiría que este post proporciona algunos datos excelentes sobre cómo responder con JSON también. El autor explica struct tags, que yo no conocía.

Desde JSON normalmente no se parece a {"Test": "test", "SomeKey": "SomeVal"}, sino a {"test": "test", "somekey": "some value"}, puedes reestructurar tu estructura de esta manera:

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

...y ahora su manejador analizará JSON usando " some-key "en lugar de" someKey " (que usará internamente).

 14
Author: JohnnyCoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-23 14:26:25