【问题标题】:Make a URL-encoded POST request using `http.NewRequest(...)`使用 `http.NewRequest(...)` 发出 URL 编码的 POST 请求
【发布时间】:2021-12-17 16:48:21
【问题描述】:

我想向 API 发出 POST 请求,将我的数据作为 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)
}

当我回复时,我总是收到400 BAD REQUEST。我相信问题取决于我的请求,并且 API 不了解我发布的有效负载。我知道Request.ParseForm 之类的方法,但不确定如何在这种情况下使用它。也许我错过了一些进一步的标头,也许有更好的方法使用body 参数将有效负载作为application/json 类型发送?

【问题讨论】:

    标签: http go


    【解决方案1】:

    必须在http.NewRequest(method, urlStr string, body io.Reader) 方法的body 参数上提供URL 编码的有效负载,作为实现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")
    
        resp, _ := client.Do(r)
        fmt.Println(resp.Status)
    }
    

    resp.Status 就是200 OK

    【讨论】:

    • 如果我不想发送任何数据怎么办?如果我发送任何虚拟数据来代替`bytes.NewBufferString(data.Encode())`,它会起作用吗?
    • 我会尝试发送一个空的 *Buffer:例如b 给定var b bytes.Buffer
    • 您不需要设置 Content-Lenght 标头,因为 http.NewRequest 已经这样做了。
    • 我认为我们可以使用strings.NewReader(data.Encode())(更高效)而不是bytes.NewBufferString(data.Encode())。在func NewReader(s string) *Reader,它说“NewReader 返回一个从 s 读取的新 Reader。它类似于 bytes.NewBufferString,但更高效且只读。”
    • data.Set 应该用于namesurname,而不是data.Add。它设置键的值,而不是像data.Add 那样将另一个值附加到同一个键。 Add 也可以工作,但不需要 append(v[key], value) 来清空切片。
    猜你喜欢
    • 2018-09-22
    • 2017-07-22
    • 2022-12-08
    • 2011-05-18
    • 2015-05-26
    • 1970-01-01
    • 2022-01-05
    • 2017-02-04
    • 2017-06-02
    相关资源
    最近更新 更多