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

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)

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

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)

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.


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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.