¿Cómo entregar una respuesta JSON usando Go?

95

Pregunta: Actualmente estoy imprimiendo mi respuesta de func Index esta fmt.Fprintf(w, string(response)) manera, sin embargo, ¿cómo puedo enviar JSON correctamente en la solicitud para que pueda consumirla una vista?

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "net/http"
    "log"
    "encoding/json"
)

type Payload struct {
    Stuff Data
}
type Data struct {
    Fruit Fruits
    Veggies Vegetables
}
type Fruits map[string]int
type Vegetables map[string]int


func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    response, err := getJsonResponse();
    if err != nil {
        panic(err)
    }
    fmt.Fprintf(w, string(response))
}


func main() {
    router := httprouter.New()
    router.GET("/", Index)
    log.Fatal(http.ListenAndServe(":8080", router))
}

func getJsonResponse()([]byte, error) {
    fruits := make(map[string]int)
    fruits["Apples"] = 25
    fruits["Oranges"] = 10

    vegetables := make(map[string]int)
    vegetables["Carrats"] = 10
    vegetables["Beets"] = 0

    d := Data{fruits, vegetables}
    p := Payload{d}

    return json.MarshalIndent(p, "", "  ")
}
Matthew Harwood
fuente
github.com/unrolled/render también puede ayudar.
elithrar

Respuestas:

128

Puede configurar su encabezado de tipo de contenido para que los clientes sepan que esperan json

w.Header().Set("Content-Type", "application/json")

Otra forma de ordenar una estructura a json es construir un codificador usando el http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)
dm03514
fuente
11
Si bien w.Header().Set("Content-Type", "application/json")es correcto para configurar el tipo de contenido, no lo hace cuando lo uso, en su json.NewEncoderlugar obtengo un resultado txt / simple. ¿Alguien más está recibiendo esto? La respuesta de @poorva funcionó como se esperaba
Jaybeecave
2
Rasca eso. Si lo uso w.WriteHeader(http.StatusOk) , obtengo el resultado anterior.
Jaybeecave
4
Si uso w.WriteHeader(http.StatusOk), obtengo text/plain; charset=utf-8, si no configuro el código de estado explícitamente, obtengo applicaton/jsony la respuesta todavía tiene un código de estado 200.
Ramon Rambo
1
Hmmm ... ¿podría tener que ver con los documentos aquí ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza
2
Añadiendo el trabajo w.Header().Set("Content-Type", "application/json")anterior json.NewEncoder(w).Encode(p)para mí
Ardi Nusawan
35

Otros usuarios comentan que Content-Typees plain/textal codificar. Content-TypePrimero debe configurar el w.Header().Setcódigo de respuesta HTTP w.WriteHeader.

Si llama w.WriteHeaderprimero, llame w.Header().Setdespués de recibir plain/text.

Un manejador de ejemplo podría verse así;

func SomeHandler(w http.ResponseWriter, r *http.Request) {
    data := SomeStruct{}
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(data)
}
Daniel R.
fuente
¿Cómo devolver la respuesta, si mi programa entra en pánico? Intenté usar recovery (), luego regresé de su pero no funcionó.
infiniteLearner
28

Puedes hacer algo como esto en tu getJsonResponsefunción:

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
pobre
fuente
2
Una nota importante sobre esta versión es que utiliza un segmento de bytes jData, posiblemente innecesariamente. Datapuede ser de tamaño arbitrario, dependiendo de los datos que se están ordenando, por lo que esto podría ser un desperdicio de memoria no trivial. Después de la clasificación, copiamos de la memoria a la ResponseWritersecuencia. La respuesta que usa json.NewEncoder (), etc.escribiría el JSON ordenado directamente en ResponseWriter(en su flujo ..)
Jonno
1
¡Trabajó para mi! Se enfrentó al problema cuando se agregó 'w.WriteHeader (http.StatusCreated)' antes o después.
darkdefender27
1
No es necesario regresar después del pánico, ya que esto sale de su programa
andersfylling
Al menos esta solución no agrega el final \ n de la Encoder.Encode()función
Jonathan Muller
2

En el marco gobuffalo.io lo hice funcionar así:

// say we are in some resource Show action
// some code is omitted
user := &models.User{}
if c.Request().Header.Get("Content-type") == "application/json" {
    return c.Render(200, r.JSON(user))
} else {
    // Make user available inside the html template
    c.Set("user", user)
    return c.Render(200, r.HTML("users/show.html"))
}

y luego, cuando quiero obtener una respuesta JSON para ese recurso, tengo que configurar "Content-type" en "application / json" y funciona.

Creo que Rails tiene una forma más conveniente de manejar múltiples tipos de respuesta, no vi lo mismo en gobuffalo hasta ahora.

Aleks Tkachenko
fuente
0

Puede usar este renderizador de paquetes , he escrito para resolver este tipo de problema, es un contenedor para servir JSON, JSONP, XML, HTML, etc.


fuente