Wie kann ich eine JSON-Antwort mit Go bereitstellen?

88

Frage: Derzeit drucke ich meine Antwort func Index wie fmt.Fprintf(w, string(response)) folgt aus. Wie kann ich JSON in der Anforderung ordnungsgemäß senden, damit es möglicherweise von einer Ansicht verwendet wird?

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
quelle
github.com/unrolled/render kann ebenfalls hilfreich sein.
Elithrar

Antworten:

122

Sie können Ihren Inhaltstyp-Header so festlegen, dass Clients wissen, dass sie json erwarten

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

Eine andere Möglichkeit, eine Struktur für json zu marshallen, besteht darin, einen Encoder mit dem zu erstellen http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)
dm03514
quelle
10
Während w.Header().Set("Content-Type", "application/json")das Einstellen des Inhaltstyps korrekt ist, wird bei Verwendung json.NewEncoderstattdessen kein txt / plain-Ergebnis angezeigt. Bekommt sonst noch jemand das? Die Antwort von @poorva funktionierte wie erwartet
Jaybeecave
2
Vergiss das. Wenn ich benutze, w.WriteHeader(http.StatusOk) bekomme ich das obige Ergebnis.
Jaybeecave
4
Wenn ich benutze, w.WriteHeader(http.StatusOk)bekomme ich text/plain; charset=utf-8, wenn ich den Status-Code nicht explizit setze, bekomme ich applicaton/jsonund die Antwort hat immer noch einen Status-Code 200.
Ramon Rambo
1
Hmmm ... könnte es mit den Dokumenten hier zu tun haben ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza
2
Hinzufügen der w.Header().Set("Content-Type", "application/json")obigen json.NewEncoder(w).Encode(p)Arbeit für mich
Ardi Nusawan
33

Andere Benutzer kommentieren , dass das Content-Typeist , plain/textwenn codiert. Sie müssen Content-Typezuerst w.Header().Setden HTTP-Antwortcode und dann den HTTP-Antwortcode festlegen w.WriteHeader.

Wenn Sie w.WriteHeaderzuerst anrufen, rufen w.Header().SetSie an, nachdem Sie erhalten haben plain/text.

Ein Beispielhandler könnte so aussehen.

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)
}
k3mist
quelle
Wie kann ich eine Antwort zurückgeben, wenn mein Programm in Panik gerät? Ich habe versucht, recovery () zu verwenden und bin dann von ihrem zurückgekehrt, aber es hat nicht geklappt.
infiniteLearner
25

Sie können so etwas in Ihrer getJsonResponseFunktion tun -

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
Poorva
quelle
2
Ein wichtiger Hinweis zu dieser Version ist, dass jDatamöglicherweise unnötig ein Byte-Slice verwendet wird. Datakann abhängig von den gemarshallten Daten eine beliebige Größe haben, so dass dies eine nicht triviale Speicherverschwendung sein kann. Nach dem Marshalling kopieren wir aus dem Speicher in den ResponseWriterStream. Die Antwort, die json.NewEncoder () usw. verwendet, würde den gemarshallten JSON direkt in den ResponseWriter(in seinen Stream ..)
Jonno
1
Hat für mich gearbeitet! Das Problem trat auf, als 'w.WriteHeader (http.StatusCreated)' vorher oder nachher hinzugefügt wurde.
darkdefender27
1
Keine Notwendigkeit, nach Panik zurückzukehren, da dies Ihr Programm beendet
andersfylling
Zumindest fügt diese Lösung nicht das Trailing \ n der Encoder.Encode()Funktion hinzu
Jonathan Muller
2

Im gobuffalo.io Framework habe ich es so gemacht:

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

und wenn ich dann eine JSON-Antwort für diese Ressource erhalten möchte, muss ich "Content-type" auf "application / json" setzen und es funktioniert.

Ich denke, Rails hat eine bequemere Möglichkeit, mit mehreren Antworttypen umzugehen. Bisher habe ich bei Gobuffalo nicht dasselbe gesehen.

Aleks Tkachenko
quelle
0

Sie können diesen Paket-Renderer verwenden . Ich habe geschrieben, um diese Art von Problem zu lösen. Es ist ein Wrapper für JSON, JSONP, XML, HTML usw.


quelle