Зробіть запит POST, кодований URL-адресою, використовуючи `http.NewRequest (…)`


96

Я хочу зробити запит POST до API, який надсилає мої дані як application/x-www-form-urlencodedтип вмісту. Через те, що мені потрібно керувати заголовками запитів, я використовую http.NewRequest(method, urlStr string, body io.Reader)метод для створення запиту. Для цього запиту POST я додаю свій запит даних до URL-адреси і залишаю тіло порожнім, приблизно так:

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

Відповідаючи, я завжди отримую a 400 BAD REQUEST. Я вважаю, що проблема покладається на мій запит, і API не розуміє, який корисний набір я публікую. Мені відомі такі методи, як Request.ParseForm, але я не дуже впевнений, як використовувати їх у цьому контексті. Можливо, мені не вистачає якогось подальшого заголовка, можливо, є кращий спосіб надіслати корисне навантаження як application/jsonтип за допомогою bodyпараметра?

Відповіді:


184

Корисна навантаження, кодована URL-адресою, повинна бути вказана в bodyпараметрі http.NewRequest(method, urlStr string, body io.Reader)методу як тип, що реалізує io.Readerінтерфейс.

На основі зразка коду:

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.Statusце 200 OKтаким чином.


2
що якщо я не хочу надсилати будь-які дані ?? Якщо я надішлю фіктивні дані замість `bytes.NewBufferString (data.Encode ())`, це спрацює?
Адітя Пешаве

Я спробував би надіслати порожній * буфер: напр. bДаноvar b bytes.Buffer
Фернандо Á.

4
Вам не потрібно встановлювати Content-Lenghtзаголовок, як http.NewRequestце вже робиться.
dvdplm

12
Я думаю, що ми можемо використовувати strings.NewReader(data.Encode())(ефективніше) замість bytes.NewBufferString(data.Encode()). У func NewReader (s string) * Reader написано: "NewReader повертає нове читання Reader із s. Це схоже на bytes.NewBufferString, але більш ефективне і лише для читання."
Liyang Chen

1
data.Setслід використовувати як для, так nameі surname, замість data.Add. Він встановлює значення ключа, замість того, щоб додавати інше значення до того самого ключа, як data.Addце робить. Addтакож працює, але робить непотрібним append(v[key], value)спорожнення фрагмента.
metalim
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.