Come fornire una risposta JSON utilizzando Go?


95

Domanda: Attualmente sto stampando la mia risposta in func Index questo fmt.Fprintf(w, string(response)) modo, tuttavia, come posso inviare correttamente JSON nella richiesta in modo che possa essere consumato da 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, "", "  ")
}

Risposte:


128

Puoi impostare l'intestazione del tipo di contenuto in modo che i clienti sappiano di aspettarsi json

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

Un altro modo per eseguire il marshalling di una struttura in json è creare un codificatore utilizzando il http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)

11
Sebbene w.Header().Set("Content-Type", "application/json")sia corretto per l'impostazione del tipo di contenuto, non lo fa quando si utilizza json.NewEncoderinvece ottengo un risultato txt / plain. Qualcun altro lo sta capendo. La risposta di @poorva ha funzionato come previsto
Jaybeecave

2
Grattalo. Se uso w.WriteHeader(http.StatusOk) ottengo il risultato di cui sopra.
Jaybeecave

4
Se lo uso , w.WriteHeader(http.StatusOk)ottengo text/plain; charset=utf-8, se non imposto il codice di stato esplicitamente ottengo applicaton/jsone la risposta ha ancora un codice di stato 200.
Ramon Rambo

1
Hmmm ... potrebbe avere a che fare con i documenti qui ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza

2
Aggiunta del lavoro w.Header().Set("Content-Type", "application/json")sopra json.NewEncoder(w).Encode(p)per me
Ardi Nusawan

35

Altri utenti commentano che Content-Typeè plain/textdurante la codifica. Devi impostare Content-Typeprima w.Header().Set, quindi il codice di risposta HTTP w.WriteHeader.

Se chiami w.WriteHeaderprima, chiama w.Header().Setdopo aver ricevuto plain/text.

Un gestore di esempio potrebbe essere simile a questo;

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

Come restituire la risposta, se il mio programma va nel panico? Ho provato a utilizzare recover (), quindi tornare da loro ma non ha funzionato.
infiniteLearner

28

Puoi fare qualcosa del genere nella tua getJsonResponsefunzione -

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)

2
Una nota importante su questa versione è che utilizza una fetta di byte in jData, forse inutilmente. Datapuò essere di dimensioni arbitrarie, a seconda dei dati di cui si esegue il marshalling, quindi potrebbe essere una perdita di memoria non banale. Dopo il marshalling, copiamo dalla memoria al ResponseWriterflusso. La risposta che utilizza json.NewEncoder () ecc. Scriverà il JSON con marshalling direttamente nel ResponseWriter(nel suo flusso ..)
Jonno

1
Ha funzionato per me! Ha affrontato il problema quando "w.WriteHeader (http.StatusCreated)" veniva aggiunto prima o dopo.
darkdefender27

1
Non c'è bisogno di tornare dopo il panico in quanto questo esce dal tuo programma
e il

Almeno questa soluzione non aggiunge il finale \ n della Encoder.Encode()funzione
Jonathan Muller

2

In gobuffalo.io framework ho capito che funziona in questo modo:

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

e poi quando voglio ottenere la risposta JSON per quella risorsa devo impostare "Content-type" su "application / json" e funziona.

Penso che Rails abbia un modo più conveniente per gestire più tipi di risposta, finora non ho visto lo stesso in gobuffalo.


0

Puoi usare questo renderer di pacchetti , ho scritto per risolvere questo tipo di problema, è un wrapper per servire JSON, JSONP, XML, HTML ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.