วิธีตอบสนอง JSON โดยใช้ Go?


95

คำถาม:ขณะนี้ฉันกำลังพิมพ์คำตอบของฉันใน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

คำตอบ:


128

คุณสามารถตั้งค่าส่วนหัวประเภทเนื้อหาเพื่อให้ลูกค้าทราบว่าจะต้องใช้ 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

2
เกานั่นแหละ ถ้าฉันใช้w.WriteHeader(http.StatusOk) ฉันจะได้ผลลัพธ์ข้างต้น
Jaybeecave

4
ถ้าฉันใช้w.WriteHeader(http.StatusOk)แล้วฉันจะได้รับtext/plain; charset=utf-8ถ้าฉันไม่ได้ตั้งค่า Status-Code อย่างชัดเจนฉันได้รับapplicaton/jsonและการตอบสนองยังคงมี Status-Code 200 อยู่
Ramon Rambo

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)ให้ฉัน
Ardi Nusawan

35

คนอื่นแสดงความคิดเห็นว่าContent-Typeเป็นplain/textเมื่อการเข้ารหัส คุณจะต้องตั้งค่าContent-Typeแรกแล้วรหัสการตอบสนองของw.Header().Set HTTPw.WriteHeader

ถ้าคุณเรียกใช้w.WriteHeaderครั้งแรกแล้วโทรหลังจากที่คุณจะได้รับw.Header().Setplain/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)
}

จะตอบกลับอย่างไรหากโปรแกรมของฉันตื่นตระหนก ฉันลองใช้การกู้คืน () จากนั้นกลับจากไฟล์เหล่านั้น แต่ไม่ได้ผล
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(ลงในสตรีม .. )
Jonno

1
ทำงานให้ฉัน! ประสบปัญหาเมื่อมีการเพิ่ม "w.WriteHeader (http.StatusCreated)" ก่อนหรือหลัง
darkdefender27

1
ไม่จำเป็นต้องกลับมาหลังจากตื่นตระหนกเพราะจะออกจากโปรแกรมของคุณ
60

อย่างน้อยวิธีนี้ก็ไม่ได้เพิ่มส่วนต่อท้าย \ n ของEncoder.Encode()ฟังก์ชัน
Jonathan Muller

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.