ฉันจะส่งสตริง JSON ในคำขอ POST ใน Go ได้อย่างไร


244

ฉันพยายามทำงานกับ Apiary และสร้างแม่แบบสากลเพื่อส่ง JSON ไปยังเซิร์ฟเวอร์จำลองและมีรหัสนี้:

package main

import (
    "encoding/json"
    "fmt"
    "github.com/jmcvetta/napping"
    "log"
    "net/http"
)

func main() {
    url := "http://restapi3.apiary.io/notes"
    fmt.Println("URL:>", url)

    s := napping.Session{}
    h := &http.Header{}
    h.Set("X-Custom-Header", "myvalue")
    s.Header = h

    var jsonStr = []byte(`
{
    "title": "Buy cheese and bread for breakfast."
}`)

    var data map[string]json.RawMessage
    err := json.Unmarshal(jsonStr, &data)
    if err != nil {
        fmt.Println(err)
    }

    resp, err := s.Post(url, &data, nil, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("response Status:", resp.Status())
    fmt.Println("response Headers:", resp.HttpResponse().Header)
    fmt.Println("response Body:", resp.RawText())

}

รหัสนี้ส่ง JSON ไม่ถูกต้อง แต่ฉันไม่รู้ว่าทำไม สตริง JSON สามารถแตกต่างกันในทุกการโทร ฉันไม่สามารถใช้Structสิ่งนี้ได้


ฉันไม่คุ้นเคยกับห้องสมุดที่คุณใช้ แต่เมื่อฉันเข้าใจคุณกำลังพยายามส่งแผนที่ของ Jsons ทำไมคุณไม่ส่งสตริงด้วย json?
โทโป

1
ทำไมคุณถึงยกเลิกการกระจาย json ถ้าคุณต้องการส่ง json
JimB

เคล็ดลับเล็กน้อยคุณสามารถสร้างข้อความของคุณเป็น struct หรือ map [string] interface {} เพื่อเพิ่มค่าทั้งหมดที่คุณต้องการจากนั้นใช้ json.Marshall เพื่อแปลงแผนที่หรือ struct เป็น json
โทโป

@topo, ฉันขุดลงในซอร์สโค้ดของ napping หากมีการตั้งค่า payload ไว้พวกเขาเรียกjson.Marshallมันว่าฉันไม่แน่ใจว่าทำไมมันไม่ทำงานกับเขา
OneOfOne

คำตอบ:


502

ฉันไม่คุ้นเคยกับการงีบหลับ แต่การใช้แพ็คเกจของ Golang net/httpทำงานได้ดี ( สนามเด็กเล่น ):

func main() {
    url := "http://restapi3.apiary.io/notes"
    fmt.Println("URL:>", url)

    var jsonStr = []byte(`{"title":"Buy cheese and bread for breakfast."}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Set("X-Custom-Header", "myvalue")
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("response Status:", resp.Status)
    fmt.Println("response Headers:", resp.Header)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println("response Body:", string(body))
}

1
ตอนนี้มันมีความตื่นตระหนกในสนามเด็กเล่น คุณเป็นคนที่กล้าแก้ไขหรืออัปเดตบางอย่างได้ไหม
Altenrion

9
@ หากไม่สามารถทำงานในสนามเด็กเล่นได้ฉันใช้มันเพื่อวางโค้ดคุณไม่สามารถเปิดการเชื่อมต่อภายนอกจากมันได้
OneOfOne

8
@Altenrion +1 สำหรับคำแนะนำชื่อวงดนตรีที่เป็นของแข็ง
Charlie Schliesser

8
เพียงเตือนอย่าลืมว่าโดยค่าเริ่มต้นไคลเอนต์ http golang ไม่เคยหมดเวลาดังนั้นสำหรับโลกแห่งความจริงที่ดีที่สุดในการตั้งค่าบางอย่างตามแนวของclient.Timeout = time.Second * 15
svarlamov

1
สามารถอัปเดตนี้เพื่อรวบรวม / ตรวจสอบข้อผิดพลาดทั้งหมดได้หรือไม่ นี่คือ (อย่างน้อยสำหรับฉัน) ผลลัพธ์อันดับต้น ๆ ของ google สำหรับการโพสต์คำขอใน Go และเป็นคำตอบที่ดี แต่ฉันเห็นโค้ดตัวอย่างจำนวนมากที่ไม่สนใจข้อผิดพลาดและฉันคิดว่ามันเป็นการกระตุ้นการปฏิบัติที่ไม่ดีในมือใหม่ จากนั้นอีกครั้งหากใครก็ตามละเลยข้อผิดพลาดเป็นประจำฉันคิดว่าพวกเขาจะเรียนรู้ว่าทำไมไม่ทำในที่สุด แต่ทำไมไม่สนับสนุนการเริ่มต้นที่ดี
K. Rhoda

103

คุณสามารถใช้postเพื่อโพสต์ json ของคุณ

values := map[string]string{"username": username, "password": password}

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))

3
ฉันได้รับข้อผิดพลาดนี้:cannot use jsonValue (type []byte) as type io.Reader in argument to http.Post: []byte does not implement io.Reader (missing Read method)
Mandar Vaze

@MandarVaze ฉันคิดว่าคุณอาจได้รับไม่ถูกต้องio.Readerสำหรับhttp.Postและ bytes.NewBuffer () ทำงานได้ดีในรหัสของฉัน
gaozhidf

1
ฉันไป 1.7 ถ้ามันสำคัญ รหัสที่แสดงโดย @OneOfOne ใช้งานได้ (ซึ่งใช้bytes.NewBuffer()แต่ใช้http.NewRequestแทนhttp.Post)
Mandar Vaze

2
ตามgolang.org/pkg/net/http/#Post "ผู้โทรควรปิดresp.Bodyเมื่ออ่านเสร็จแล้วหากเนื้อหาที่ระบุเป็นตัวio.Closerมันจะถูกปิดหลังจากคำขอ" ฉันจะบอกได้อย่างไรว่าเป็นมือใหม่ถ้าร่างกายเป็นร่างกายio.Closerหรือกล่าวอีกนัยหนึ่งถ้าตัวอย่างนี้ปลอดภัย?
Dennis

14

หากคุณมีโครงสร้าง

type Student struct {
    Name    string `json:"name"`
    Address string `json:"address"`
}

// .....

body := &Student{
    Name:    "abc",
    Address: "xyz",
}

buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(body)
req, _ := http.NewRequest("POST", url, buf)

client := &http.Client{}
res, e := client.Do(req)
if e != nil {
    return e
}

defer res.Body.Close()

fmt.Println("response Status:", res.Status)
// Print the body to the stdout
io.Copy(os.Stdout, res.Body)

เต็มส่วนสำคัญ


12

นอกเหนือจากแพ็คเกจ net / http มาตรฐานแล้วคุณสามารถลองใช้GoRequestซึ่งล้อมรอบ net / http และทำให้ชีวิตของคุณง่ายขึ้นโดยไม่ต้องคิดมากเกี่ยวกับ json หรือ struct แต่คุณสามารถผสมและจับคู่ทั้งคู่ในคำขอเดียว! (คุณสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในหน้า Gorequest GitHub)

ดังนั้นในที่สุดรหัสของคุณจะเป็นดังนี้:

func main() {
    url := "http://restapi3.apiary.io/notes"
    fmt.Println("URL:>", url)
    request := gorequest.New()
    titleList := []string{"title1", "title2", "title3"}
    for _, title := range titleList {
        resp, body, errs := request.Post(url).
            Set("X-Custom-Header", "myvalue").
            Send(`{"title":"` + title + `"}`).
            End()
        if errs != nil {
            fmt.Println(errs)
            os.Exit(1)
        }
        fmt.Println("response Status:", resp.Status)
        fmt.Println("response Headers:", resp.Header)
        fmt.Println("response Body:", body)
    }
}

ขึ้นอยู่กับวิธีที่คุณต้องการบรรลุ ฉันสร้างห้องสมุดนี้เพราะฉันมีปัญหาเดียวกันกับคุณและฉันต้องการรหัสที่สั้นกว่าใช้งานง่ายด้วย json และสามารถบำรุงรักษาได้มากขึ้นใน codebase และระบบการผลิตของฉัน


ถ้า GoRequest ล้อม net / http เป็นไปได้หรือไม่ที่จะเพิ่มสิ่งนี้เพื่อปิดใช้งานใบรับรองที่ไม่ปลอดภัยสำหรับ TLS tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388

46
@ user1513388 เป็นความคิดที่ดีเสมอที่จะมีส่วนร่วมในตัวอย่างโค้ดของการข้ามการตรวจสอบ TLS ในทุกสถานการณ์ในภาษาใด ๆ ... คุณทำให้สำเนา / วาง "การแก้ไขปัญหา" โดย neophytes ที่เยี่ยมชม StackOverflow โดยไม่ตั้งใจและไม่เข้าใจถึงสาเหตุของการแก้ไขข้อผิดพลาด TLS เป็นสิ่งสำคัญ แก้ไขเส้นทางการนำเข้าใบรับรองของคุณ (หากใช้การเซ็นชื่อด้วยตนเองสำหรับการทดสอบนำเข้าสิ่งเหล่านั้น) หรือแก้ไขห่วงโซ่ใบรับรองของเครื่องหรือค้นหาสาเหตุที่เซิร์ฟเวอร์ของคุณแสดงใบรับรองที่ไม่ถูกต้องซึ่งลูกค้าของคุณไม่สามารถตรวจสอบได้
Mike Atlas

1
สิ่งหนึ่งที่ฉันไม่ชอบเกี่ยวกับคำตอบนี้คือวิธีที่มันรวบรวมวัตถุ JSON ซึ่งอาจถูกใช้ประโยชน์จากการฉีด วิธีที่ดีกว่าคือการเขียนวัตถุแล้วแปลงเป็น JSON (ด้วยการหลบหนีที่เหมาะสม)
John White

@ JohnWhite ฉันเห็นด้วยรู้สึกทับทิมมาก / js / pythonic
Rambatino
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.