Stellen Sie eine URL-codierte POST-Anfrage mit "http.NewRequest (…)"

92

Ich möchte eine POST-Anfrage an eine API senden, die meine Daten als application/x-www-form-urlencodedInhaltstyp sendet . Aufgrund der Tatsache, dass ich die Anforderungsheader verwalten muss, verwende ich die http.NewRequest(method, urlStr string, body io.Reader)Methode, um eine Anforderung zu erstellen. Für diese POST-Anfrage hänge ich meine Datenabfrage an die URL an und lasse den Text leer, ungefähr so:

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

Als Antwort bekomme ich immer eine 400 BAD REQUEST. Ich glaube, das Problem hängt von meiner Anfrage ab und die API versteht nicht, welche Nutzdaten ich poste. Ich kenne Methoden wie Request.ParseForm, bin mir aber nicht sicher, wie ich sie in diesem Zusammenhang verwenden soll. Vielleicht fehlt mir ein weiterer Header, vielleicht gibt es eine bessere Möglichkeit, Nutzdaten als application/jsonTyp mithilfe des bodyParameters zu senden ?

Fernando Á.
quelle

Antworten:

181

URL-codierte Nutzdaten müssen für den bodyParameter der http.NewRequest(method, urlStr string, body io.Reader)Methode als Typ angegeben werden, der die io.ReaderSchnittstelle implementiert .

Basierend auf dem Beispielcode:

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.Statusist 200 OKso.

Fernando Á.
quelle
1
Was ist, wenn ich keine Daten senden möchte? Wenn ich Dummy-Daten anstelle von "bytes.NewBufferString (data.Encode ())" sende, funktioniert das?
Aditya Peshave
Ich würde versuchen, einen leeren * Puffer zu senden: zB bgegebenvar b bytes.Buffer
Fernando Á.
4
Sie müssen den Content-LenghtHeader nicht setzen , wie http.NewRequestdas bereits.
dvdplm
12
Ich denke, wir können strings.NewReader(data.Encode())(effizienter) anstelle von verwenden bytes.NewBufferString(data.Encode()). Bei func NewReader (s string) * Reader heißt es: "NewReader gibt einen neuen Reader-Messwert von s zurück. Er ähnelt bytes.NewBufferString, ist jedoch effizienter und schreibgeschützt."
Liyang Chen
1
data.Setsollte für beide nameund surnameanstelle von verwendet werden data.Add. Es legt den Wert des Schlüssels fest, anstatt wie üblich einen anderen Wert an denselben Schlüssel data.Addanzuhängen. Addfunktioniert auch, ist aber nicht notwendig, append(v[key], value)um Slice zu leeren.
Metalim