Effettuare una richiesta POST con codifica URL utilizzando "http.NewRequest (…)"


96

Voglio fare una richiesta POST a un'API inviando i miei dati come application/x-www-form-urlencodedtipo di contenuto. A causa del fatto che devo gestire le intestazioni della richiesta, sto utilizzando il http.NewRequest(method, urlStr string, body io.Reader)metodo per creare una richiesta. Per questa richiesta POST aggiungo la mia query di dati all'URL e lascio il corpo vuoto, qualcosa del genere:

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

Quando rispondo, ottengo sempre un 400 BAD REQUEST. Credo che il problema dipenda dalla mia richiesta e l'API non capisce quale payload sto pubblicando. Sono a conoscenza di metodi come Request.ParseForm, non sono proprio sicuro di come usarlo in questo contesto. Forse mi manca qualche ulteriore intestazione, forse c'è un modo migliore per inviare il payload come application/jsontipo utilizzando il bodyparametro?

Risposte:


184

Il payload con codifica URL deve essere fornito nel bodyparametro del http.NewRequest(method, urlStr string, body io.Reader)metodo, come un tipo che implementa l' io.Readerinterfaccia.

Basato sul codice di esempio:

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è in 200 OKquesto modo.


2
cosa succede se non voglio inviare alcun dato? Se invio dei dati fittizi al posto di `bytes.NewBufferString (data.Encode ())`, funzionerà?
Aditya Peshave

Proverei a inviare un * Buffer vuoto: es. bDatovar b bytes.Buffer
Fernando Á.

4
Non è necessario impostare l' Content-Lenghtintestazione, come http.NewRequestgià fa.
dvdplm

12
Penso che possiamo usare strings.NewReader(data.Encode())(più efficiente) invece di bytes.NewBufferString(data.Encode()). In func NewReader (s string) * Reader , viene visualizzato "NewReader restituisce una nuova lettura di Reader da s. È simile a bytes.NewBufferString ma più efficiente e di sola lettura".
Liyang Chen

1
data.Setdovrebbe essere usato per entrambi namee surname, invece di data.Add. Imposta il valore della chiave, invece di aggiungere un altro valore alla stessa chiave, come data.Addfa. Addfunziona anche, ma non è necessario append(v[key], value)svuotare la slice.
metalim
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.