Выполните запрос GET и создайте строку запроса

89

Я новичок в Go и еще не совсем все понимаю. Во многих современных языках Node.js, Angular, jQuery, PHP вы можете выполнять запрос GET с дополнительными параметрами строки запроса.

Сделать это в Go не так просто, как кажется, и я пока не могу понять этого. Я действительно не хочу объединять строку для каждого запроса, который я хочу выполнить.

Вот пример сценария:

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

В этом примере вы можете увидеть, что есть URL-адрес, для которого требуется переменная GET api_key с вашим ключом api в качестве значения. Проблема в том, что это становится жестко закодированным в форме:

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

Есть ли способ динамически построить эту строку запроса? На данный момент мне нужно будет собрать URL-адрес до этого шага, чтобы получить действительный ответ.

Руди Стридом
источник
1
Так что плохого в объединении строки?
Сальвадор Дали
4
Я ничего не думаю, но это не совсем изящное решение, просто подумал, что есть лучший способ делать что-то в Go. Вы видите, как меняется действие, метод, а затем вам нужно связать все вместе.
Rudi Strydom
2
Вы можете использовать url.Values«S Encodeметод. Вы также можете использовать URL.Stringдля создания всего URL.
Dave C

Ответы:

213

Как уже упоминалось комментатор вы можете получить Valuesиз net/urlкоторых имеет Encodeметод. Вы могли бы сделать что-то вроде этого ( req.URL.Query()возвращает существующий 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
источник
1
Отличное спасибо, чувак! Это именно то, что я искал!
Rudi Strydom
Отлично и для тестирования!
Кайл Чадха
Любое решение сделать параметры строки запроса в желаемом порядке? Ключи отсортированы по Encode()методам.
artificerpi
1
@artificerpi, если это было критично по какой-то причине, вы могли бы заново реализовать то, что они делают, внутри метода Encode golang.org/src/net/url/url.go?s=24222:24253#L845 Но мне интересно, почему это имело значение.
jcbwlkr
3
Вам не нужно использовать NewRequest, если вы ничего с ним не делаете. Вы можете просто использовать url.Parse("https://something.com/")вместо него или даже создать объект URL напрямую.
Ричард
35

Используйте r.URL.Query()при добавлении к существующему запросу, если вы создаете новый набор параметров, используйте такую url.Valuesструктуру

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
}
аллираза
источник
14

Использование NewRequestтолько для создания URL-адреса - излишество. Используйте net/urlпакет:

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

Площадка: https://play.golang.org/p/YCTvdluws-r

Янек Ольшак
источник
Согласен, отличный ответ!
pow-il
Это действительно должен быть принятый ответ.
Пол