Goを使用してJSON応答を提供する方法は?


94

質問:現在、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/unrolled/renderも役立つ場合があります。
elithrar 2015

回答:


126

クライアントが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 /無地結果を得ます。他の誰かがこれを手に入れていますか?@poorvaからの回答は期待どおりに機能しました
Jaybeecave 2017

2
それをスクラッチします。使用するw.WriteHeader(http.StatusOk) と上記の結果が得られます。
Jaybeecave 2017

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.
ダンエスパルザ2017年

2
w.Header().Set("Content-Type", "application/json")上記のjson.NewEncoder(w).Encode(p)作業を追加する
Ardi Nusawan 2018年

34

他のユーザーContent-Typeplain/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)
}

プログラムがパニックになった場合、応答を返す方法は?私はrecover()を使用してみて、それから彼らから戻ってきましたが、うまくいきませんでした。
infiniteLearner

27

あなたはあなたのgetJsonResponse機能でこのようなことをすることができます-

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

2
このバージョンに関する重要な注意点の1つは、でバイトスライスを使用するjDataことです。おそらく不必要です。Dataマーシャリングされるデータに応じて、任意のサイズにすることができるため、これは重要なメモリの浪費になる可能性があります。マーシャリング後、メモリからResponseWriterストリームにコピーします。json.NewEncoder()などを使用する答えは、マーシャリングされたJSONをResponseWriter(そのストリームに..)
直接書き込み

1
私のために働いた!'w.WriteHeader(http.StatusCreated)'が前または後に追加されたときに問題に直面しました。
darkdefender27 2017

1
これはプログラムを終了するため、パニックの後に戻る必要はありません
2017年

少なくともこのソリューションでは、Encoder.Encode()関数の末尾に\ nは追加されません
JonathanMuller19年

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などを提供するラッパーです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.