Bagaimana cara menyajikan respons JSON menggunakan Go?

97

Pertanyaan: Saat ini saya mencetak tanggapan saya func Index seperti ini. fmt.Fprintf(w, string(response)) Namun, bagaimana saya dapat mengirim JSON dengan benar dalam permintaan sehingga mungkin dikonsumsi oleh tampilan?

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
sumber
github.com/unrolled/render juga dapat membantu.
elithrar

Jawaban:

130

Anda dapat mengatur header tipe konten Anda sehingga klien tahu untuk mengharapkan json

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

Cara lain untuk menyusun struct ke json adalah dengan membuat encoder menggunakan http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)
dm03514
sumber
11
Meskipun w.Header().Set("Content-Type", "application/json")benar untuk mengatur jenis konten, tidak saat menggunakan json.NewEncodersebaliknya saya mendapatkan hasil txt / polos. Apakah ada orang lain yang mengerti ini. Jawaban dari @poorva bekerja seperti yang diharapkan
Jaybeecave
2
Gores itu. Jika saya menggunakan w.WriteHeader(http.StatusOk) saya mendapatkan hasil di atas.
Jaybeecave
4
Jika saya menggunakan w.WriteHeader(http.StatusOk)maka saya mendapatkan text/plain; charset=utf-8, jika saya tidak mengatur Kode-Status secara eksplisit saya dapatkan applicaton/jsondan Respons masih memiliki Kode-Status 200.
Ramon Rambo
1
Hmmm ... mungkinkah itu ada hubungannya dengan dokumen di sini ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza
2
Menambahkan pekerjaan di w.Header().Set("Content-Type", "application/json")atas json.NewEncoder(w).Encode(p)untuk saya
Ardi Nusawan
35

Pengguna lain berkomentar bahwa Content-Typeis plain/textsaat encoding. Anda harus mengatur yang Content-Typepertama w.Header().Set, kemudian kode respon HTTP w.WriteHeader.

Jika Anda menelepon w.WriteHeaderdulu kemudian menelepon w.Header().Setsetelah Anda akan mendapatkan plain/text.

Contoh penangan mungkin terlihat seperti ini;

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.
sumber
Bagaimana cara membalas, jika program saya panik? Saya mencoba menggunakan recover (), lalu kembali dari mereka tetapi tidak berhasil.
infiniteLearner
28

Anda dapat melakukan sesuatu seperti ini dalam getJsonResponsefungsi Anda -

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
malang
sumber
2
Satu catatan penting tentang versi ini adalah ia menggunakan potongan byte jData, mungkin tidak perlu. Dataukurannya bisa berubah-ubah, bergantung pada data yang sedang disusun, jadi ini bisa menjadi pemborosan memori yang tidak sepele. Setelah menyusun, kami menyalin dari memori ke ResponseWriteraliran. Jawaban yang menggunakan json.NewEncoder () dll. Akan menulis JSON yang tersusun langsung ke ResponseWriter(ke dalam alirannya ..)
Jonno
1
Bekerja untuk saya! Menghadapi masalah saat 'w.WriteHeader (http.StatusCreated)' ditambahkan sebelum atau sesudah.
darkdefender
1
Tidak perlu kembali setelah panik karena ini keluar dari program Anda
andersfylling
Setidaknya solusi ini tidak menambahkan trailing \ n dari Encoder.Encode()fungsi
Jonathan Muller
2

Dalam kerangka gobuffalo.io saya membuatnya berfungsi seperti ini:

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

dan kemudian ketika saya ingin mendapatkan respons JSON untuk sumber daya itu, saya harus menyetel "Jenis konten" ke "application / json" dan berhasil.

Saya pikir Rails memiliki cara yang lebih nyaman untuk menangani berbagai jenis respons, sejauh ini saya tidak melihat hal yang sama di gobuffalo.

Aleks Tkachenko
sumber
0

Anda dapat menggunakan perender paket ini , saya telah menulis untuk menyelesaikan masalah semacam ini, ini adalah pembungkus untuk melayani JSON, JSONP, XML, HTML dll.


sumber