Comment servir une réponse JSON en utilisant Go?

95

Question: Je suis actuellement imprimer ma réponse dans le func Index comme cela fmt.Fprintf(w, string(response)) cependant, comment puis - je envoyer JSON correctement dans la demande de sorte qu'il peut être consommé par une vue?

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
la source
github.com/unrolled/render peut également vous aider.
elithrar

Réponses:

128

Vous pouvez définir votre en-tête de type de contenu pour que les clients sachent s'attendre à json

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

Une autre façon de marshaler une structure en json consiste à créer un encodeur en utilisant le http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)
dm03514
la source
11
Bien que ce w.Header().Set("Content-Type", "application/json")soit correct pour définir le type de contenu, ce n'est pas le cas lors de l'utilisation à la json.NewEncoderplace, j'obtiens un résultat txt / plain. Est-ce que quelqu'un d'autre comprend cela. La réponse de @poorva a fonctionné comme prévu
Jaybeecave
2
Grattez ça. Si j'utilise, w.WriteHeader(http.StatusOk) j'obtiens le résultat ci-dessus.
Jaybeecave
4
Si j'utilise w.WriteHeader(http.StatusOk)alors j'obtiens text/plain; charset=utf-8, si je ne règle pas explicitement le code d'état, j'obtiens applicaton/jsonet la réponse a toujours un code d'état 200.
Ramon Rambo
1
Hmmm ... cela pourrait-il avoir à voir avec les documents ici ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza
2
Ajout du travail w.Header().Set("Content-Type", "application/json")ci-dessus json.NewEncoder(w).Encode(p)pour moi
Ardi Nusawan
35

D'autres utilisateurs commentent que le Content-Typeest plain/textlors de l'encodage. Vous devez définir le Content-Typepremier w.Header().Set, puis le code de réponse HTTP w.WriteHeader.

Si vous appelez en w.WriteHeaderpremier, puis appelez w.Header().Setaprès vous obtiendrez plain/text.

Un exemple de gestionnaire pourrait ressembler à ceci;

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.
la source
Comment retourner la réponse, si mon programme panique? J'ai essayé d'utiliser recover (), puis de revenir de leur mais cela n'a pas fonctionné.
infiniteLearner
28

Vous pouvez faire quelque chose comme ça dans votre getJsonResponsefonction -

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
pauvre
la source
2
Une remarque importante à propos de cette version est qu'elle utilise une tranche d'octet jData, peut-être inutilement. Datapeut être de taille arbitraire, en fonction des données rassemblées, donc cela pourrait être un gaspillage de mémoire non trivial. Après le rassemblement, nous copions de la mémoire vers le ResponseWriterflux. La réponse qui utilise json.NewEncoder () etc. écrirait le JSON marshallé directement dans le ResponseWriter(dans son flux ..)
Jonno
1
A travaillé pour moi! Face au problème lorsque 'w.WriteHeader (http.StatusCreated)' a été ajouté avant ou après.
darkdefender27
1
Pas besoin de revenir après la panique car cela quitte votre programme
andersfylling
Au moins cette solution n'ajoute pas la fin \ n de la Encoder.Encode()fonction
Jonathan Muller
2

Dans le framework gobuffalo.io, je l'ai fait fonctionner comme ceci:

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

puis quand je veux obtenir une réponse JSON pour cette ressource, je dois définir "Content-type" sur "application / json" et cela fonctionne.

Je pense que Rails a un moyen plus pratique de gérer plusieurs types de réponses, je n'ai pas vu la même chose dans gobuffalo jusqu'à présent.

Aleks Tkachenko
la source
0

Vous pouvez utiliser ce rendu de package , j'ai écrit pour résoudre ce genre de problème, c'est un wrapper pour servir JSON, JSONP, XML, HTML etc.


la source