Làm cách nào để gửi chuỗi JSON trong yêu cầu POST trong Go


244

Tôi đã thử làm việc với Aperator và tạo một mẫu phổ quát để gửi JSON đến máy chủ giả và có mã này:

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

}

Mã này không gửi JSON đúng cách, nhưng tôi không biết tại sao. Chuỗi JSON có thể khác nhau trong mỗi cuộc gọi. Tôi không thể sử dụng Structcho việc này.


Tôi không quen thuộc với một số thư viện bạn sử dụng, nhưng theo tôi hiểu, bạn đang cố gắng gửi bản đồ của Jsons. Tại sao bạn không gửi chuỗi với json?
Topo

1
Tại sao bạn lại sắp xếp json nếu bạn muốn gửi json?
JimB

Một mẹo nhỏ, bạn có thể tạo tin nhắn của mình dưới dạng giao diện struct hoặc chuỗi [}] để thêm tất cả các giá trị bạn muốn và sau đó sử dụng json.Marshall để chuyển đổi bản đồ hoặc struct sang json.
Topo

@topo, tôi đã đào sâu vào mã nguồn của ngủ trưa, nếu tải trọng được đặt, họ gọi json.Marshallnó, tôi không chắc tại sao nó không hoạt động cho anh ta.
OneOfOne

Câu trả lời:


500

Tôi không quen với việc ngủ trưa, nhưng sử dụng net/httpgói của Golang hoạt động tốt ( sân chơi ):

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
bây giờ nó có hoảng loạn trên sân chơi. Có thể bạn sẽ sửa chữa hoặc cập nhật một cái gì đó?
Altenrion

9
@Altenrion nó không thể hoạt động trên sân chơi, tôi chỉ sử dụng nó để dán mã, bạn không thể mở các kết nối bên ngoài từ nó.
OneOfOne

8
@Altenrion +1 cho đề xuất tên ban nhạc vững chắc.
Charlie Schliesser 30/03/18

8
Chỉ là một cảnh báo, đừng quên rằng theo mặc định, ứng dụng khách golang http không bao giờ hết thời gian, vì vậy đối với thế giới thực, tốt nhất là đặt một cái gì đó dọc theo dòngclient.Timeout = time.Second * 15
svarlamov

1
Điều này có thể được cập nhật để thu thập / kiểm tra tất cả các lỗi không? Đây là (đối với tôi, ít nhất) là kết quả hàng đầu trên google khi thực hiện các yêu cầu bài đăng trong Go và đó là một câu trả lời tốt, nhưng tôi thấy một tấn mã ví dụ chỉ bỏ qua lỗi và tôi nghĩ rằng nó khuyến khích thực hành xấu ở người mới. Sau đó, một lần nữa, nếu bất cứ ai thường xuyên bỏ qua lỗi, tôi cho rằng họ sẽ học tại sao cuối cùng lại không, nhưng tại sao không khuyến khích thực hành tốt để bắt đầu?
K. Rhoda

103

bạn chỉ có thể sử dụng postđể đăng json của bạn.

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

jsonValue, _ := json.Marshal(values)

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

3
Tôi nhận được lỗi này: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 tôi nghĩ rằng bạn có thể nhận sai io.Readercho http.Post, và bytes.NewBuffer () hoạt động tốt trong mã của tôi
gaozhidf

1
Tôi đang đi 1.7, nếu nó quan trọng. Mã được liệt kê bởi @OneOfOne hoạt động (cũng sử dụng bytes.NewBuffer()nhưng sử dụng http.NewRequestthay vì http.Post)
Mandar Vaze

2
Theo golang.org/pkg/net/http/#Post , "Người gọi nên đóng resp.Bodykhi đọc xong từ nó. Nếu cơ thể được cung cấp là một io.Closer, nó sẽ bị đóng sau khi yêu cầu." Làm thế nào tôi có thể nói, với tư cách là một người mới chơi cờ vây, nếu cơ thể là một io.Closer, hay nói cách khác, nếu ví dụ này an toàn?
Dennis

14

Nếu bạn đã có một cấu trúc.

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)

Full ý chính .


12

Ngoài gói net / http tiêu chuẩn, bạn có thể xem xét sử dụng GoRequest của tôi bao quanh net / http và làm cho cuộc sống của bạn dễ dàng hơn mà không phải suy nghĩ quá nhiều về json hoặc struct. Nhưng bạn cũng có thể trộn và kết hợp cả hai trong một yêu cầu! (bạn có thể xem thêm chi tiết về nó trong trang github của gorequest)

Vì vậy, cuối cùng, mã của bạn sẽ trở nên như sau:

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

Điều này phụ thuộc vào cách bạn muốn đạt được. Tôi đã tạo thư viện này bởi vì tôi có cùng một vấn đề với bạn và tôi muốn mã ngắn hơn, dễ sử dụng với json và dễ bảo trì hơn trong hệ thống sản xuất và cơ sở mã của tôi.


Nếu GoRequest kết thúc mạng / http. Có thể thêm điều này để vô hiệu hóa chứng chỉ Không an toàn cho TLS không? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
dùng1513388

46
@ user1513388 Luôn luôn là một ý tưởng khủng khiếp khi đóng góp các ví dụ mã về bỏ qua xác minh TLS trong bất kỳ tình huống nào bằng bất kỳ ngôn ngữ nào ... bạn vô tình duy trì rất nhiều bản sao / dán "cách giải quyết" của những người truy cập StackOverflow và không hiểu bản chất của việc sửa lỗi Lỗi TLS là rất quan trọng. Khắc phục đường dẫn nhập chứng chỉ của bạn (nếu sử dụng tự ký để kiểm tra, nhập chúng) hoặc sửa chuỗi chứng chỉ của máy hoặc tìm hiểu lý do tại sao máy chủ của bạn xuất trình chứng chỉ không hợp lệ mà khách hàng của bạn không thể xác minh.
Mike Atlas

1
Một điều tôi không thích chính xác về câu trả lời này là cách nó tạo ra đối tượng JSON, có khả năng khai thác thông qua tiêm. Một cách tốt hơn sẽ là soạn một đối tượng và sau đó chuyển đổi nó thành JSON (với cách thoát phù hợp).
John White

@JohnWhite Tôi đồng ý, cảm thấy rất ruby ​​/ js / pythonic
Rambatino
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.