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

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)

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

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)

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.


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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.