Ve haciendo una solicitud GET y construyendo la Querystring

85

Soy bastante nuevo en Go y todavía no lo entiendo todo. En muchos de los lenguajes modernos Node.js, Angular, jQuery, PHP, puede hacer una solicitud GET con parámetros de cadena de consulta adicionales.

Hacer esto en Go no es tan simple como parece, y todavía no puedo resolverlo. Realmente no quiero tener que concatenar una cadena para cada una de las solicitudes que quiero hacer.

Aquí está el script de muestra:

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

En este ejemplo, puede ver que hay una URL, que requiere una variable GET de api_key con su clave api como valor. El problema es que esto se codifica de forma rígida en forma de:

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

¿Hay alguna forma de construir esta cadena de consulta de forma dinámica? Por el momento, necesitaré ensamblar la URL antes de este paso para obtener una respuesta válida.

Rudi Strydom
fuente
1
Entonces, ¿qué hay de malo en concatenar una cadena?
Salvador Dali
4
Supongo que nada, pero no es realmente una solución elegante, solo pensé que hay una mejor manera de hacer las cosas en Go. Ves que la acción cambia, el método y luego tienes que unir todo.
Rudi Strydom
2
Puede utilizar url.Valuesel Encodemétodo de. También puede utilizar URL.Stringpara crear la URL completa.
Dave C

Respuestas:

208

Como mencionó un comentarista, puede obtener Valuesde lo net/urlque tiene un Encodemétodo. Podrías hacer algo como esto ( req.URL.Query()devuelve el 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
fuente
1
¡Increíble gracias hombre! ¡Eso es exactamente lo que estaba buscando!
Rudi Strydom
¡Genial para probar también!
Kyle Chadha
¿Alguna solución para hacer que los parámetros de la cadena de consulta estén en el orden deseado? Las claves se ordenan por Encode()método.
artificerpi
1
@artificerpi, si fuera crítico por alguna razón, podría volver a implementar lo que hacen dentro del método Encode golang.org/src/net/url/url.go?s=24222:24253#L845 Pero me pregunto por qué importa .
jcbwlkr
3
No necesita usar NewRequest si no está haciendo nada con él. En su url.Parse("https://something.com/")lugar, puede usar o incluso crear un objeto URL directamente.
Richard
34

Úselo r.URL.Query()cuando agregue a una consulta existente, si está creando un nuevo conjunto de parámetros, use la url.Valuesestructura así

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
fuente
11

Usar NewRequestsolo para crear una URL es una exageración. Usa el net/urlpaquete:

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

Zona de juegos: https://play.golang.org/p/YCTvdluws-r

Janek Olszak
fuente
respuesta más simple y funcionó perfecto, gracias
Jake Boomgaarden