【Go入門】net/urlパッケージによるURLのパースと生成

シェアする

こんにちは。Go入門ブログの第25回です。

前々回の記事で、net/httpパッケージを利用したWEBサーバの構築方法と、HTTPリクエストの基本的な処理方法を解説しました。

クライアント側のリクエスト処理も解説したいのですが、今回の記事ではそれに先立って、net/urlパッケージについて解説したいと思います。
net/urlパッケージは、URLのパースや生成など、URL文字列を処理する機能を提供する標準パッケージです。

URLをパースする

URLのパースには、関数url.Parseを使用します。
関数url.Parseは、引数で渡されたURL文字列からurl.URL構造体を生成します。

url.Parse(URL文字列 string) (URL構造体, err os.Error)

引数で渡すURL文字列は、相対パスでも絶対パスで可です。
ただし、当然ではありますが、ホスト名を含めたURLパースを行う場合は絶対パスである必要があります。

url.URL構造体のフィールド

続いて、url.Parseによりパースしたurl.URL構造体のフィールドを見てみましょう。

type URL struct {
    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)
}

このサンプルの実行結果は以下のようになります。

$ go run main.go
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文字列に再生成します。
日本語ドメインはエンコードするなどのエスケープ処理が行われます。

func (u *URL) String() string

サンプルと実行結果は以下のようになります。

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)
    
}
$ go run main.go
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) Hostname() string

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メソッド
    
}
$ go run main.go
Host: sample.com:8080
Hostname: sample.com
Port: 8080
RequestURIメソッド

RequestURIメソッドは、厳密な意味でのURIではなく、JSPなどでいうところのコンテキストパスを返すメソッドです。
大まかに、パスとクエリを連結した文字列を返すと考えてよいでしょう。

func (u *URL) RequestURI() string

サンプルと実行結果は以下のようになります。

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)
    
}
$ go run main.go
/golang/neturl?q1=foo&q2=bar
Queryメソッド

Queryメソッドは、RawQueryフィールドのクエリパラメータを解析し、キーとクエリ値をペアにしたマップ型を返します。

func (u *URL) Query() Values

サンプルと実行結果は以下のようになります。

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)
    
}
$ go run main.go
map[q1:[foo] q2:[bar]]

なお、Queryメソッドで生成されたマップ型は、クエリパラメータを操作する為の独自メソッドを持ちます。
これについては、後ほどURLの生成について解説する際に例示します。

IsAbsメソッド

IsAbsメソッドメソッドは、URLが絶対パスで記述されているか否かを判定します。
絶対パスである場合はtrueを、相対パスである場合はfalseを返します。

func (u *URL) IsAbs() bool

サンプルと実行結果は以下のようになります。

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())
    
}
$ go run main.go
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が出力されます。

$ go run main.go
https://generate.com:8080/golang/neturl?q1=english&q2=%E6%97%A5%E6%9C%AC%E8%AA%9E

終わりに

今回の内容はさほど難しくなかったのではないでしょうか。
net/urlパッケージでは、url.URL構造体を利用することで、URLのパースや生成をシンプルかつ直感的に行えることがおわかりいただけたかと思います。

URL操作は以降の記事でも使用することになりますので、必要に応じて今回の記事を参照していただければ幸いです。

スポンサーリンク
スポンサーリンク
スポンサーリンク

シェアする

フォローする

スポンサーリンク
スポンサーリンク