Wykonaj żądanie GET i zbuduj Querystring

85

Jestem całkiem nowy w Go i jeszcze nie do końca wszystko rozumiem. W wielu współczesnych językach Node.js, Angular, jQuery, PHP można wykonać żądanie GET z dodatkowymi parametrami ciągu zapytania.

Robienie tego w Go nie jest takie proste, jak się wydaje, i nie mogę jeszcze tego rozgryźć. Naprawdę nie chcę łączyć łańcucha dla każdego żądania, które chcę wykonać.

Oto przykładowy skrypt:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    client := &http.Client{}

    req, _ := http.NewRequest("GET", "http://api.themoviedb.org/3/tv/popular", nil)
    req.Header.Add("Accept", "application/json")
    resp, err := client.Do(req)

    if err != nil {
        fmt.Println("Errored when sending request to the server")
        return
    }

    defer resp.Body.Close()
    resp_body, _ := ioutil.ReadAll(resp.Body)

    fmt.Println(resp.Status)
    fmt.Println(string(resp_body))
}

W tym przykładzie widać, że istnieje adres URL, który wymaga zmiennej GET api_key z kluczem api jako wartością. Problem polega na tym, że zostaje to zakodowane w postaci:

req, _ := http.NewRequest("GET", "http://api.themoviedb.org/3/tv/popular?api_key=mySuperAwesomeApiKey", nil)

Czy istnieje sposób dynamicznego tworzenia tego ciągu zapytania? W tej chwili będę musiał zebrać adres URL przed tym krokiem, aby uzyskać prawidłową odpowiedź.

Rudi Strydom
źródło
1
Więc co jest złego w konkatenacji łańcucha?
Salvador Dali
4
Przypuszczam, że nic, ale tak naprawdę nie jest to eleganckie rozwiązanie, po prostu pomyślałem, że istnieje lepszy sposób robienia rzeczy w Go. Widzisz, jak zmienia się akcja, metoda, a potem musisz wszystko połączyć.
Rudi Strydom
2
Możesz użyć metody url.Values'' Encode. Możesz również użyć URL.Stringdo zbudowania całego adresu URL.
Dave C

Odpowiedzi:

208

Jak wspomniał komentator, możesz uzyskać, Valuesz net/urlktórego ma Encodemetodę. Możesz zrobić coś takiego ( req.URL.Query()zwraca istniejący url.Values)

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    req, err := http.NewRequest("GET", "http://api.themoviedb.org/3/tv/popular", nil)
    if err != nil {
        log.Print(err)
        os.Exit(1)
    }

    q := req.URL.Query()
    q.Add("api_key", "key_from_environment_or_flag")
    q.Add("another_thing", "foo & bar")
    req.URL.RawQuery = q.Encode()

    fmt.Println(req.URL.String())
    // Output:
    // http://api.themoviedb.org/3/tv/popular?another_thing=foo+%26+bar&api_key=key_from_environment_or_flag
}

http://play.golang.org/p/L5XCrw9VIG

jcbwlkr
źródło
1
Wspaniale dziękuję człowieku! Właśnie tego szukałem!
Rudi Strydom
Świetne również do testowania!
Kyle Chadha
Jakieś rozwiązanie, dzięki któremu parametry ciągu zapytania będą miały pożądaną kolejność? Klucze są posortowane według Encode()metody.
artificerpi
1
@artificerpi, jeśli z jakiegoś powodu było to krytyczne, możesz ponownie zaimplementować to, co robią, w metodzie Encode golang.org/src/net/url/url.go?s=24222:24253#L845 .
jcbwlkr
3
Nie musisz używać NewRequest, jeśli nic z nim nie robisz. Możesz po prostu użyć url.Parse("https://something.com/")zamiast tego lub nawet bezpośrednio utworzyć obiekt URL.
Richard
34

Użyj r.URL.Query()podczas dołączania do istniejącego zapytania, jeśli budujesz nowy zestaw parametrów, użyj url.Valuesstruktury w ten sposób

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/url"
    "os"
)

func main() {
    req, err := http.NewRequest("GET","http://api.themoviedb.org/3/tv/popular", nil)
    if err != nil {
        log.Print(err)
        os.Exit(1)
    }

    // if you appending to existing query this works fine 
    q := req.URL.Query()
    q.Add("api_key", "key_from_environment_or_flag")
    q.Add("another_thing", "foo & bar")

    // or you can create new url.Values struct and encode that like so
    q := url.Values{}
    q.Add("api_key", "key_from_environment_or_flag")
    q.Add("another_thing", "foo & bar")

    req.URL.RawQuery = q.Encode()

    fmt.Println(req.URL.String())
    // Output:
    // http://api.themoviedb.org/3/tv/popularanother_thing=foo+%26+bar&api_key=key_from_environment_or_flag
}
allyraza
źródło
12

Używanie NewRequestsamego tylko do tworzenia adresu URL to przesada. Skorzystaj z net/urlpakietu:

package main

import (
    "fmt"
    "net/url"
)

func main() {
    base, err := url.Parse("http://www.example.com")
    if err != nil {
        return
    }

    // Path params
    base.Path += "this will get automatically encoded"

    // Query params
    params := url.Values{}
    params.Add("q", "this will get encoded as well")
    base.RawQuery = params.Encode() 

    fmt.Printf("Encoded URL is %q\n", base.String())
}

Plac zabaw: https://play.golang.org/p/YCTvdluws-r

Janek Olszak
źródło
najprostsza odpowiedź i działała idealnie, dzięki
Jake Boomgaarden