Go言語(プログラミング)入門メモ㉗

## HTTPリクエストの送信
Webページなどでデータをやりとりする際は、HTTPという取り決めに従う必要がある。Goの標準パッケージを利用して、HTTPリクエストを送信する。

## Getリクエストの送信
net/httpは、WebサーバやWebクライアントを作るためのパッケージ。
http.Get関数では、指定したURLにGETリクエストを送信できる。ここでは、「http:<foo>//example.com」にアクセスし、返り値を変数respに格納する。エラーハンドリングは行わず、_でエラーは無視する。
また、Getリクエストの結果であるレスポンスボディは、クローズする必要があるので「defer resp.Body.Close()」を実行しておく。
次に「io.ReadAll(resp.Body)」で読み取った内容を変数bodyに代入する。最後に、「fmt.Println(string(body))」で表示すると、アクセスしたURLのHTMLが表示される。
~~~
package main

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

func main() {
	resp, _ := http.Get("http://example.com")
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
~~~
※補足
急に出てきたresp.Bodyってなんだと思ったので調べると、
respは、HTTPステータスコード、ヘッダー、および本文を含むHTTPレスポンス全体を表すオブジェクトで、HTTPレスポンスの本文（ボディ）のみを表したのがresp.Bodyということだった。
※補足終わり

結果は以下のように表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/5e673b11-80f5-7057-f23c-2cd36e2145de.png)

## net/urlパッケージでURLの解析を行う。
URLが正しい形式か解析するには、net/urlパッケージをインポートしてurl.Parse関数を使う。url.Parse関数は、引数に渡した文字列を解析し、返り値としてURL型の構造体を返す。文字列がURLの形式でなかればエラーを返す。
「http:<foo>//example.<foo/>com」を解析してみる。
~~~
package main

import (
	"fmt"
	"net/url"
)

func main() {
	base, err := url.Parse("http://example.com")
	fmt.Println(base, err)
}
~~~
結果は以下のように表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/14957393-ef82-31d6-eced-9d393488c903.png)
URL部分を編集して、エラーが起こるようにしてみる。
~~~
package main

import (
	"fmt"
	"net/url"
)

func main() {
	base, err := url.Parse("http://exam ple.com") //スペースを入れる
	fmt.Println(base, err)
}
~~~
結果は以下のように表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/c9075778-38d1-7115-538d-64e3b147edcb.png)

## クエリパラメータの追加
HTTPリクエストの際、URLの後ろに追加して渡す値のことを、クエリパラメータという。クエリパラメータによりサーバーに対して特定のリクエストを行う。
「http:<foo>//example.com/test?a=1&b=2」というURLの場合、「?a=1&b=2」のように?の後ろに追加しているのがクエリパラメータ。
このクエリパラメータは、変数aに1、変数bに2をそれぞれ指定している。
ResolveReferenceメソッドを使用するとクエリパラメータを追加することができる。
まず、url.Parse関数で「http:<foo>//example.com」のあとにつなげるURL「/test?a=1&b=2」を書き、変数referenceに代入する。
次に、変数baseと変数referenceから、アクセスするURLを生成する。「base.ResolveReference(reference)」として変数baseと変数referenceをつなげたURLを作成し、String()で文字列にして、変数endpointに代入する。
~~~
package main

import (
	"fmt"
	"net/url"
)

func main() {
	base, _ := url.Parse("http://example.com")
	reference, _ := url.Parse("/test?a=1&b=2")
	endpoint := base.ResolveReference(reference).String()
	fmt.Println(endpoint)

}
~~~
表示結果は以下のようになりクエリパラメータが追加されていることが分かる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/58da97d4-728f-f7c1-768e-8a4a1182c68c.png)

## http.NewRequest関数でリクエストを作成する
http.NewRequest関数でリクエストを作成し、Getリクエストを送信する方法を実行する。
まずは、http.NewRequest関数でリクエストを作成する。引数にはHTTPメソッドとURLをそれぞれ一つ目、二つ目に指定する。三つ目の引数は、リクエストボディ(送信するデータ)を指定するが、今回はGETメソッドなのでnilを指定。(リクエストボディはPOSTなどを使用する際に指定する。)
~~~
package main

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

func main() {
	base, _ := url.Parse("http://example.com")
	reference, _ := url.Parse("/test?a=1&b=2")
	endpoint := base.ResolveReference(reference).String()
	fmt.Println(endpoint)
	req, _ := http.NewRequest("Get", endpoint, nil)
}
~~~
上記のコードで変数reqに代入したHTTPリクエストは変数endpointに代入したURL「http://example.com/test?a=1&b=2」を指定している。このクエリパラメータは、「req/URL.Query()」のようにQueryメソッドで取り出すことができる。

~~~
package main

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

func main() {
	base, _ := url.Parse("http://example.com")
	reference, _ := url.Parse("/test?a=1&b=2")
	endpoint := base.ResolveReference(reference).String()
	fmt.Println(endpoint)
	req, _ := http.NewRequest("Get", endpoint, nil)
	q := req.URL.Query()
	fmt.Println(q)
}
~~~
以下のように表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/de833839-bddf-d7b7-aeca-933dd3c645d7.png)

## Addメソッドでクエリパラメータを追加する
http.NewRequest関数でリクエストを作成する場合はAddメソッドでクエリパラメータを追加できる。
次のコードでは。「q.Add("c","3&%")」で変数qにクエリパラメータを追加している。
変数qのクエリパラメータを、変数reqのHTTPリクエストに反映するには、「req.URL.RawQueery」に値を代入する。ただしその際にq.Encode()のようにして、Encodeメソッドを使ってエンコードする必要がある。
~~~
package main

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

func main() {
	base, _ := url.Parse("http://example.com")
	reference, _ := url.Parse("/test?a=1&b=2")
	endpoint := base.ResolveReference(reference).String()
	fmt.Println(endpoint)
	req, _ := http.NewRequest("Get", endpoint, nil)
	q := req.URL.Query()
	q.Add("c", "3&%")
	fmt.Println(q)
	fmt.Println(q.Encode())
	req.URL.RawQuery = q.Encode()
}
~~~
実行結果は以下のように表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/fb913c18-ac01-9c25-93fa-3a624f621f69.png)
マップを見ると、クエリパラメータに「c:[3&%]」が追加されていることが分かる。一方エンコードしたクエリパラメータは、「c=3%26%25」のように「&」が「%26」、「%」が「%25」にエンコードされていることが分かる。(URL表記では&は区切り文字なのでそのまま入れることはできない。%も同様)

## http.ClientでHTTPリクエストを送信する
作成したHTTPリクエストを使ってGETリクエストを行うにはhttp.Clientという型でクライアントを作る必要がある。
「var client *http.Client = &http.Client{}」というクライアントを宣言し、「clinet.Do(req)」で変数reqのHTTPリクエストを実行し、実行結果を変数respに代入する。
io.ReadAllで内容を読み取ると「http://example.com」の内容が表示される。
(このページはクエリパラメータを渡してもレスポンスに変化がない)
~~~
package main

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

func main() {
	base, _ := url.Parse("http://example.com")
	reference, _ := url.Parse("/test?a=1&b=2")
	endpoint := base.ResolveReference(reference).String()
	fmt.Println(endpoint)
	req, _ := http.NewRequest("Get", endpoint, nil)
	q := req.URL.Query()
	q.Add("c", "3&%")
	fmt.Println(q)
	fmt.Println(q.Encode())
	req.URL.RawQuery = q.Encode()

	var client *http.Client = &http.Client{}
	resp, _ := client.Do(req)
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
~~~
結果は以下のように表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/5e673b11-80f5-7057-f23c-2cd36e2145de.png)
## 学習に使用した教材
・『入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』M.A EduTech
https://www.udemy.com/course/golang-webgosql/?utm_medium=udemyads&utm_source=bene-msa&utm_campaign=responsive&utm_content=top-1&utm_term=general&msclkid=81e2f24a32cc185d275d953d60760226&couponCode=NEWYEARCAREERJP

・『シリコンバレー一流プログラマーが教える Goプロフェッショナル大全』酒井 潤 (著)
https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%90%E3%83%AC%E3%83%BC%E4%B8%80%E6%B5%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B-Go%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E5%A4%A7%E5%85%A8-%E9%85%92%E4%BA%95-%E6%BD%A4/dp/4046070897