Jak wyświetlić odpowiedź JSON za pomocą Go?

104

Pytanie: Obecnie drukuję moją odpowiedź w func Index ten fmt.Fprintf(w, string(response)) sposób, jednak w jaki sposób mogę poprawnie wysłać JSON w żądaniu, aby mógł zostać wykorzystany przez widok?

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
źródło
Pomocne może być również github.com/unrolled/render .
elithrar

Odpowiedzi:

132

Możesz ustawić nagłówek typu zawartości, aby klienci wiedzieli, że oczekują JSON

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

Innym sposobem organizowania struktury do json jest utworzenie kodera przy użyciu http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)
dm03514
źródło
12
Chociaż w.Header().Set("Content-Type", "application/json")jest poprawny do ustawiania typu zawartości, nie działa, gdy używam json.NewEncoderzamiast tego otrzymuję wynik w formacie txt / zwykły. Czy ktoś inny to łapie. Odpowiedź z @poorva zadziałała zgodnie z oczekiwaniami
Jaybeecave
2
Podrap to. Jeśli używam w.WriteHeader(http.StatusOk) , otrzymuję powyższy wynik.
Jaybeecave
4
Jeśli w.WriteHeader(http.StatusOk)użyję text/plain; charset=utf-8, otrzymam , jeśli nie ustawię jawnie kodu statusu, otrzymam, applicaton/jsona odpowiedź ma nadal kod stanu 200.
Ramon Rambo
2
Hmmm ... czy to może mieć coś wspólnego z obecnymi tu dokumentami ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza
2
Dodanie w.Header().Set("Content-Type", "application/json")powyższej json.NewEncoder(w).Encode(p)pracy dla mnie
Ardi Nusawan
37

Inni użytkownicy komentując, że Content-Typejest plain/textprzy kodowaniu. Musisz ustawić Content-Typepierwszy w.Header().Set, a następnie kod odpowiedzi HTTP w.WriteHeader.

Jeśli zadzwonisz w.WriteHeaderpierwszy, zadzwoń w.Header().Setpo otrzymaniu plain/text.

Przykładowy program obsługi może wyglądać następująco;

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.
źródło
Jak zwrócić odpowiedź, jeśli mój program panikuje? Próbowałem użyć polecenia restore (), a następnie wróciłem z ich, ale nie wyszło.
infiniteLearner
31

Możesz zrobić coś takiego w swojej getJsonResponsefunkcji -

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
poorva
źródło
2
Jedną z ważnych uwag dotyczących tej wersji jest to, że jDataprawdopodobnie niepotrzebnie używa ona segmentu bajtów . Datamoże mieć dowolny rozmiar, w zależności od uporządkowanych danych, więc może to być nietrywialne marnotrawstwo pamięci. Po uporządkowaniu kopiujemy z pamięci do ResponseWriterstrumienia. Odpowiedź, która używa json.NewEncoder () itp. Zapisze zorganizowany JSON bezpośrednio do ResponseWriter(do jego strumienia ..)
Jonno
1
Pracował dla mnie! Rozwiązano problem, gdy „w.WriteHeader (http.StatusCreated)” zostało dodane przed lub po.
darkdefender27
1
Nie musisz wracać po panice, ponieważ kończy to twój program
andersfylling
Przynajmniej to rozwiązanie nie dodaje końcowego \ n Encoder.Encode()funkcji
Jonathan Muller
@Jonno masz rację, ale to jedyna odpowiedź, kiedy możesz sprawdzić, czy kodowanie idzie dobrze PRZED napisaniem nagłówka, ponieważ raz napisany można go zmienić!
Cirelli94
2

We frameworku gobuffalo.io mam to tak, aby działało tak:

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

a potem, gdy chcę uzyskać odpowiedź JSON dla tego zasobu, muszę ustawić „Content-type” na „application / json” i działa.

Myślę, że Railsy mają wygodniejszy sposób obsługi wielu typów odpowiedzi, nie widziałem tego samego w gobuffalo do tej pory.

Aleks Tkachenko
źródło
0

Możesz użyć tego renderera pakietów , napisałem, aby rozwiązać tego rodzaju problem, jest to opakowanie obsługujące JSON, JSONP, XML, HTML itp.


źródło