Buat permintaan POST berenkode URL menggunakan `http.NewRequest (…)`

97

Saya ingin membuat permintaan POST ke API yang mengirimkan data saya sebagai application/x-www-form-urlencodedtipe konten. Karena fakta bahwa saya perlu mengelola header permintaan, saya menggunakanhttp.NewRequest(method, urlStr string, body io.Reader) metode untuk membuat permintaan. Untuk permintaan POST ini saya menambahkan kueri data saya ke URL dan membiarkan badan kosong, seperti ini:

package main

import (
    "bytes"
    "fmt"
    "net/http"
    "net/url"
    "strconv"
)

func main() {
    apiUrl := "https://api.com"
    resource := "/user/"
    data := url.Values{}
    data.Set("name", "foo")
    data.Add("surname", "bar")

    u, _ := url.ParseRequestURI(apiUrl)
    u.Path = resource
    u.RawQuery = data.Encode()
    urlStr := fmt.Sprintf("%v", u) // "https://api.com/user/?name=foo&surname=bar"

    client := &http.Client{}
    r, _ := http.NewRequest("POST", urlStr, nil)
    r.Header.Add("Authorization", "auth_token=\"XXXXXXX\"")
    r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))

    resp, _ := client.Do(r)
    fmt.Println(resp.Status)
}

Sebagai tanggapan saya, saya selalu mendapatkan 400 BAD REQUEST. Saya yakin masalahnya bergantung pada permintaan saya dan API tidak memahami muatan mana yang saya posting. Saya mengetahui metode seperti Request.ParseForm, tidak begitu yakin bagaimana menggunakannya dalam konteks ini. Mungkin saya kehilangan beberapa Header lebih lanjut, mungkin adakah cara yang lebih baik untuk mengirim payload sebagai application/jsontipe menggunakan bodyparameter?

Fernando Á.
sumber

Jawaban:

188

Payload yang dikodekan URL harus diberikan pada bodyparameter http.NewRequest(method, urlStr string, body io.Reader)metode, sebagai jenis yang diterapkanio.Reader antarmuka.

Berdasarkan kode sampel:

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "strconv"
    "strings"
)

func main() {
    apiUrl := "https://api.com"
    resource := "/user/"
    data := url.Values{}
    data.Set("name", "foo")
    data.Set("surname", "bar")

    u, _ := url.ParseRequestURI(apiUrl)
    u.Path = resource
    urlStr := u.String() // "https://api.com/user/"

    client := &http.Client{}
    r, _ := http.NewRequest(http.MethodPost, urlStr, strings.NewReader(data.Encode())) // URL-encoded payload
    r.Header.Add("Authorization", "auth_token=\"XXXXXXX\"")
    r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))

    resp, _ := client.Do(r)
    fmt.Println(resp.Status)
}

resp.Statuslewat 200 OKsini.

Fernando Á.
sumber
2
bagaimana jika saya tidak ingin mengirim data apapun ?? Jika saya mengirim data dummy sebagai pengganti `bytes.NewBufferString (data.Encode ())`, apakah itu akan berhasil?
Aditya Peshave
Saya akan mencoba mengirim * Buffer kosong: misalnya bdiberikanvar b bytes.Buffer
Fernando Á.
4
Anda tidak perlu menyetel Content-Lenghtheader, seperti http.NewRequestyang sudah dilakukan.
dvdplm
12
Saya pikir kita bisa menggunakan strings.NewReader(data.Encode())(lebih efisien) daripada bytes.NewBufferString(data.Encode()). Di func NewReader (s string) * Reader , dikatakan "NewReader mengembalikan pembacaan Reader baru dari s. Ini mirip dengan bytes.NewBufferString tetapi lebih efisien dan hanya-baca."
Liyang Chen
1
data.Setharus digunakan untuk namedan surname, sebagai ganti data.Add. Ini menetapkan nilai kunci, daripada menambahkan nilai lain ke kunci yang sama, seperti data.Addhalnya. Addbekerja juga, tetapi tidak perlu append(v[key], value)mengosongkan slice.
metalim