Vá fazendo uma solicitação GET e construindo a Querystring

89

Eu sou muito novo em Go e não entendo muito bem tudo ainda. Em muitas das linguagens modernas Node.js, Angular, jQuery, PHP, você pode fazer uma solicitação GET com parâmetros de string de consulta adicionais.

Fazer isso no Go não é tão simples quanto parece, e eu realmente não consigo descobrir ainda. Eu realmente não quero ter que concatenar uma string para cada uma das solicitações que desejo fazer.

Aqui está o script de exemplo:

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

Neste exemplo, você pode ver que há um URL, que requer uma variável GET de api_key com sua chave de API como o valor. O problema é que isso se torna codificado na forma de:

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

Existe uma maneira de construir essa string de consulta dinamicamente ?? No momento, precisarei montar a URL antes desta etapa para obter uma resposta válida.

Rudi Strydom
fonte
1
Então, o que há de errado em concatenar uma string?
Salvador Dali
4
Suponho que nada, mas não é realmente uma solução elegante, apenas pensei que havia uma maneira melhor de fazer as coisas em Go. Você vê as mudanças de ação, o método e então você tem que amarrar tudo junto.
Rudi Strydom
2
Você pode usar url.Valueso Encodemétodo de. Você também pode usar URL.Stringpara construir a URL inteira.
Dave C

Respostas:

213

Como um comentador mencionado, você pode obter Valuesde net/urlqual tem um Encodemétodo. Você poderia fazer algo assim ( req.URL.Query()retorna o existente 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
fonte
1
Incrível, obrigado cara! Isso é exatamente o que eu estava procurando!
Rudi Strydom
Ótimo para testar também!
Kyle Chadha de
Alguma solução para fazer os parâmetros da string de consulta em sua ordem desejada? As chaves são classificadas no Encode()método.
artificerpi
1
@artificerpi se fosse crítico por algum motivo, você poderia reimplementar o que eles fazem dentro do método Encode golang.org/src/net/url/url.go?s=24222:24253#L845 Mas eu me perguntaria por que isso importa.
jcbwlkr
3
Você não precisa usar NewRequest se não estiver fazendo nada com ele. Você pode apenas usar em url.Parse("https://something.com/")vez disso ou até mesmo criar um objeto URL diretamente.
Richard
35

Use r.URL.Query()quando você anexar a uma consulta existente, se você estiver construindo um novo conjunto de parâmetros, use a url.Valuesestrutura assim

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
fonte
14

Usar NewRequestapenas para criar um URL é um exagero. Use o net/urlpacote:

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

Playground: https://play.golang.org/p/YCTvdluws-r

Janek Olszak
fonte
Concordo, ótima resposta!
pow-il
Essa realmente deveria ser a resposta aceita.
Paulo