166

I am pretty new to Go and don't quite understand everything as yet. In many of the modern languages Node.js, Angular, jQuery, PHP you can do a GET request with additional query string parameters.

Doing this in Go isn't quite a simple as it seems, and I can't really figure it out as yet. I really don't want to have to concatenate a string for each of the requests I want to do.

Here is the sample script:

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

In this example you can see there is a URL, which requires a GET variable of api_key with your api key as the value. The problem being that this becomes hard coded in the form of:

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

Is there a way to build this query string dynamically?? At the moment I will need to assemble the URL prior to this step in order to get a valid response.

3
  • 1
    So what is wrong with concatenating a string? Commented Jun 4, 2015 at 19:34
  • 11
    I suppose nothing, but it's not really a elegant sollution, just thought there is a better way of doing things in Go. You see the action changes, the method and then you have to string everything together. Commented Jun 4, 2015 at 19:40
  • 3
    You can use url.Values's Encode method. You could also use URL.String to build up the whole URL. Commented Jun 4, 2015 at 20:46

4 Answers 4

328

As a commenter mentioned you can get Values from net/url which has an Encode method. You could do something like this (req.URL.Query() returns the existing 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

Sign up to request clarification or add additional context in comments.

5 Comments

Any solution to make the query string parameters in your wanted order? The keys are sorted in Encode() method.
@artificerpi if it was critical for some reason you could reimplement what they do inside the Encode method golang.org/src/net/url/url.go?s=24222:24253#L845 But I would wonder why it mattered.
You don't need to use NewRequest if you're not doing anything with it. You can just use url.Parse("https://something.com/") instead or even create an URL object directly.
one thing that is confusing me in this is that it reads like the OP first makes the request, and then after it's finished, goes back and updates the URL. I'm sure that's not the case, so hoping someone can clear up for me why we're first making the API call and then setting the URL we need
@JakeBoomgaarden the http.NewRequest function does not actually call the remote server. It just constructs a *http.Request variable for you to use. You can tweak the request to your liking then send it off by passing it to the *http.Client.Do method.
69

Using NewRequest just to create an URL is an overkill. Use the net/url package:

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

Comments

62

Use r.URL.Query() when you appending to existing query, if you are building new set of params use the url.Values struct like so

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 } 

Comments

-1

It's work correctly and i have tested this code :

package main import ( "fmt" "io/ioutil" "log" "net/http" ) func main() { url := "https://example.com" req, err := http.NewRequest("GET", url, nil) if err != nil { log.Fatal(err) } req.Header.Add("Accept", "application/json") req.Header.Add("Api-Key", "XXXXXXXXXXXX")//<--add your header resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() // print all the response headers fmt.Println("Response headers:") for k, v := range resp.Header { fmt.Printf("%s: %s\n", k, v) } fmt.Println("Status:", resp.Status) data, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Println(string(data)) } 

It's like this PHP code :

<?php $curl_h = curl_init('https://example.com'); curl_setopt($curl_h, CURLOPT_HTTPHEADER, array( 'Api-Key: xxxxxxxxxxxxxxxx', 'Accept : application/json' ) ); # do not output, but store to variable curl_setopt($curl_h, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($curl_h); echo $response; 

1 Comment

OP is asking about adding query params not headers

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.