Як подати відповідь JSON за допомогою Go?


97

Питання: В даний час я роздруковую свою відповідь приблизно func Index так, fmt.Fprintf(w, string(response)) однак, як я можу правильно надіслати JSON у запиті, щоб він, можливо, був використаний видом?

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/unroll/render також може допомогти.
elithrar

Відповіді:


130

Ви можете встановити заголовок вмісту, щоб клієнти знали, що очікувати json

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

Інший спосіб укладання структури в json - це побудова кодера за допомогою http.ResponseWriter

// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)

11
Незважаючи на те w.Header().Set("Content-Type", "application/json"), що правильним є встановлення типу вмісту, при використанні json.NewEncoderя не отримую результат txt / plain. Хтось ще отримує це. Відповідь від @poorva спрацювала, як очікувалося
Jaybeecave

2
Подряпайте це. Якщо я використовую, w.WriteHeader(http.StatusOk) я отримую вищезазначений результат.
Jaybeecave,

4
Якщо я використовую, w.WriteHeader(http.StatusOk)тоді я отримую text/plain; charset=utf-8, якщо я не встановлюю код статусу явно, я отримую, applicaton/jsonі відповідь все ще має статус-код 200.
Рамон Рембо

1
Хммм ... чи може це бути пов’язано з документами тут ? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Dan Esparza

2
Додавання w.Header().Set("Content-Type", "application/json")вище json.NewEncoder(w).Encode(p)роботи для мене
Арді Нусаван

35

Інші користувачі коментують, що це Content-Typeсаме plain/textпри кодуванні. Потрібно встановити Content-Typeперший w.Header().Set, а потім код відповіді HTTP w.WriteHeader.

Якщо ви телефонуєте w.WriteHeaderпершим, то телефонуйте w.Header().Setпісля того, як отримаєте plain/text.

Приклад обробника може виглядати так;

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

Як повернути відповідь, якщо моя програма панікує? Я спробував використовувати recovery (), потім повернувся з їх, але це не вийшло.
infiniteLearner

28

Ви можете зробити щось подібне у своїй getJsonResponseфункції -

jData, err := json.Marshal(Data)
if err != nil {
    // handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)

2
Важливим зауваженням щодо цієї версії є те, що вона використовує фрагмент байту jData, можливо, без потреби. Dataможе мати довільний розмір, залежно від даних, що маршуються, тому це може бути нетривіальним марнотратством пам’яті. Після маршування ми копіюємо з пам'яті в ResponseWriterпотік. Відповідь, що використовує json.NewEncoder () тощо, писатиме маршальований JSON прямо в ResponseWriter(у його потік ..)
Джонно,

1
Працював у мене! Зіткнувся з проблемою, коли до або після цього було додано "w.WriteHeader (http.StatusCreated)".
darkdefender27

1
Не потрібно повертатися після паніки, оскільки це виходить із вашої програми
andersfylling

Принаймні, це рішення не додає Encoder.Encode()
Джонатан Мюллер,

2

У рамках gobuffalo.io я змусив його працювати так:

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

а потім, коли я хочу отримати відповідь JSON на цей ресурс, я повинен встановити для "Content-type" значення "application / json", і це працює.

Я думаю, що Rails має більш зручний спосіб обробляти різні типи відповідей, поки що я не бачив того ж у gobuffalo.


0

Ви можете використовувати цей пакетний візуалізатор , я писав для вирішення такого роду проблем, це обгортка для обслуговування JSON, JSONP, XML, HTML тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.