こんにちは。Go入門ブログの第25回です。
前々回の記事で、net/httpパッケージを利用したWEBサーバの構築方法と、HTTPリクエストの基本的な処理方法を解説しました。
クライアント側のリクエスト処理も解説したいのですが、今回の記事ではそれに先立って、net/urlパッケージについて解説したいと思います。
net/urlパッケージは、URLのパースや生成など、URL文字列を処理する機能を提供する標準パッケージです。
Contents
URLをパースする
URLのパースには、関数url.Parseを使用します。
関数url.Parseは、引数で渡されたURL文字列からurl.URL構造体を生成します。
引数で渡すURL文字列は、相対パスでも絶対パスで可です。
ただし、当然ではありますが、ホスト名を含めたURLパースを行う場合は絶対パスである必要があります。
url.URL構造体のフィールド
続いて、url.Parseによりパースしたurl.URL構造体のフィールドを見てみましょう。
Scheme string // スキーム
Opaque string // 不透明URL
User *Userinfo // ユーザー名とパスワード
Host string // ホスト情報
Path string // パス
RawPath string // エンコードされたパスのヒント
ForceQuery bool // クエリパラメータ
RawQuery string // クエリ値 ※?は除外
Fragment string // URLフラグメント
}
上記このように、url.URL構造体はパースしたURLのホスト情報やパス、クエリパラメータなどを各フィールドに保持します。
これらのフィールドを参照することで、URL中の任意の情報を容易に取得することができます。
サンプルを見てみましょう。
package main import ( "fmt" "net/url" ) func main() { // url.ParseによりURL文字列をパース u, err := url.Parse("https://sample@hoge.com/golang/neturl?q1=foo&q2=bar#fragment") if err != nil { fmt.Println("error: urlパース失敗") } // 取得したurl.URL構造体の各フィールドを参照 fmt.Printf("Scheme: %s\n", u.Scheme) fmt.Printf("Opaque: %s\n", u.Opaque) fmt.Printf("User: %s\n", u.User) fmt.Printf("Host: %s\n", u.Host) fmt.Printf("Path: %s\n", u.Path) fmt.Printf("RawPath: %s\n", u.RawPath) fmt.Printf("ForceQuery: %s\n", u.ForceQuery) fmt.Printf("RawQuery: %s\n", u.RawQuery) fmt.Printf("Fragment: %s\n", u.Fragment) }
このサンプルの実行結果は以下のようになります。
Scheme: https
Opaque:
User: sample
Host: hoge.com
Path: /golang/neturl
RawPath:
ForceQuery: %!s(bool=false)
RawQuery: q1=foo&q2=bar
Fragment: fragment
url.URL構造体のメソッド
url.URL構造体はフィールド以外に、URLを解析するための多くのメソッドを持ちます。
代表的なものを解説します。
Stringメソッド
Stringメソッドは、 URLを有効なURL文字列に再生成します。
日本語ドメインはエンコードするなどのエスケープ処理が行われます。
サンプルと実行結果は以下のようになります。
package main import ( "fmt" "net/url" ) func main() { // url.ParseによりURL文字列をパース u, err := url.Parse("https://日本語.com/golang/neturl?q=パラメータ") if err != nil { fmt.Println("error: urlパース失敗") } // StringメソッドでURL文字列を再生成 uStr := u.String() fmt.Println(uStr) }
https://%E6%97%A5%E6%9C%AC%E8%AA%9E.com/golang/neturl?q=パラメータ
HostnameメソッドとPortメソッド
url.URL構造体のhostフィールドは、URL中のポート番号を含めたホスト情報を参照できます。
ホスト名とポートをそれぞれ別個に取得したい場合、HostnameメソッドとPortメソッドを使用します。
func (u *URL) Port() string
サンプルと実行結果は以下のようになります。
package main import ( "fmt" "net/url" ) func main() { // url.ParseによりURL文字列をパース u, err := url.Parse("https://sample.com:8080/golang/neturl") if err != nil { fmt.Println("error: urlパース失敗") } fmt.Printf("Host: %s\n", u.Host) // Hostフィールド fmt.Printf("Hostname: %s\n", u.Hostname()) // Hostメソッド fmt.Printf("Port: %s\n", u.Port()) // Portメソッド }
Host: sample.com:8080
Hostname: sample.com
Port: 8080
RequestURIメソッド
RequestURIメソッドは、厳密な意味でのURIではなく、JSPなどでいうところのコンテキストパスを返すメソッドです。
大まかに、パスとクエリを連結した文字列を返すと考えてよいでしょう。
サンプルと実行結果は以下のようになります。
package main import ( "fmt" "net/url" ) func main() { // url.ParseによりURL文字列をパース u, err := url.Parse("https://sample.com/golang/neturl?q1=foo&q2=bar") if err != nil { fmt.Println("error: urlパース失敗") } // RequestURIでパスとクエリを取得 uri := u.RequestURI() fmt.Println(uri) }
/golang/neturl?q1=foo&q2=bar
Queryメソッド
Queryメソッドは、RawQueryフィールドのクエリパラメータを解析し、キーとクエリ値をペアにしたマップ型を返します。
サンプルと実行結果は以下のようになります。
package main import ( "fmt" "net/url" ) func main() { // url.ParseによりURL文字列をパース u, err := url.Parse("https://sample.com/golang/neturl?q1=foo&q2=bar") if err != nil { fmt.Println("error: urlパース失敗") } // Queryメソッドでクエリパラメータのマップ型を生成 query := u.Query() fmt.Println(query) }
map[q1:[foo] q2:[bar]]
なお、Queryメソッドで生成されたマップ型は、クエリパラメータを操作する為の独自メソッドを持ちます。
これについては、後ほどURLの生成について解説する際に例示します。
IsAbsメソッド
IsAbsメソッドメソッドは、URLが絶対パスで記述されているか否かを判定します。
絶対パスである場合はtrueを、相対パスである場合はfalseを返します。
サンプルと実行結果は以下のようになります。
package main import ( "fmt" "net/url" ) // IsAbsメソッドで絶対パスの判定を行う関数を定義 func decideAbs(uStr string, uAbs bool) { if uAbs { fmt.Printf("%sは絶対パスです\n", uStr) } else { fmt.Printf("%sは相対パスです\n", uStr) } } func main() { // 絶対パスで記述されたURL文字列をパース u1, _ := url.Parse("https://sample.com/golang/neturl?q1=foo&q2=bar#fragment") // 相対パスで記述されたURL文字列をパース u2, _ := url.Parse("golang/neturl?q1=foo&q2=bar#fragment") // 関数decideAbsの実行 decideAbs(u1.String(), u1.IsAbs()) decideAbs(u2.String(), u2.IsAbs()) }
https://sample.com/golang/neturl?q1=foo&q2=bar#fragmentは絶対パスです
golang/neturl?q1=foo&q2=bar#fragmentは相対パスです
任意のURLを生成する
ここまで、文字列形式で取得したURLをパースして操作・参照する方法をみてきました。
それでは、任意のURLを生成したい場合はどのようにすれば良いでしょうか。
URLの生成にも、url.URL構造体を使用します。
この場合、空のurl.URL構造体のポインタを変数に格納し、構造体の各フィールドに値を設定していくことになります。
サンプルを見てみましょう。
package main import ( "fmt" "net/url" ) func main() { // 空のurl.URL構造体のポインタ u := &url.URL{} // 構造体のフィールドに値をセット u.Scheme = "https" u.Host = "generate.com:8080" u.Path = "golang/neturl" // Queryメソッドでクエリパラメータのマップを取得 q := u.Query() // クエリマップに値をセット q.Set("q1", "english") q.Set("q2", "日本語") // クエリパラメータをエンコード u.RawQuery = q.Encode() // 生成したURLを出力 fmt.Println(u) }
Queryメソッドで取得したマップ型に定義された独自メソッドを使用している点に注目してください。
20-21行目のq.Setと、23行目のq.Encodeがそれにあたります。
Setメソッドは、クエリのキーを指定してクエリ値の設定・書き換えを行うことができます。
Encodeメソッドは、クエリ文字列をURLエンコード形式にエンコードします。
このサンプルを実行すると、以下のように生成したURLが出力されます。
https://generate.com:8080/golang/neturl?q1=english&q2=%E6%97%A5%E6%9C%AC%E8%AA%9E
終わりに
今回の内容はさほど難しくなかったのではないでしょうか。
net/urlパッケージでは、url.URL構造体を利用することで、URLのパースや生成をシンプルかつ直感的に行えることがおわかりいただけたかと思います。
URL操作は以降の記事でも使用することになりますので、必要に応じて今回の記事を参照していただければ幸いです。