URL也是一個結構體:安全
type URL struct { Scheme string Opaque string // encoded opaque data User *Userinfo // username and password information Host string // host or host:port Path string // path (relative paths may omit leading slash) RawPath string // encoded path hint (see EscapedPath method) ForceQuery bool // append a query ('?') even if RawQuery is empty RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' }
URL結構表示解析以後的URL,通常格式爲:app
[scheme:][//[userinfo@]host][/]path[?query][#fragment]
因爲path和query部分只能使用大小寫字母、數字以及有限的幾個特殊標點,其它全部的字符都須要進行URL編碼:百分號+2位16進制數。例如,空格被編碼爲"%20",斜線被編碼爲"%2f",有時候query的value中空格會被編碼爲"+"。函數
例如,query部分被編碼後的內容以下:post
name=Hiram%20Veeblefeetzer&age=35&country=Madagascar+abc
它表示3個key/value:name="Hiram Veeblefeetzer"
、age=35
、country=Madagascar abc
。編碼
關於URL,其中:url
URL.Hostname
、URL.Port
URL.User.Username()
,URL.User.Password()
使用URL的Parse(string)
方法能夠將字符串構形成一個URL對象,並返回這個URL對象的指針。指針
如今使用這個方法來構造一個URL對象,並解析其中的各個部分:code
package main import "fmt" import "net/url" func main() { // 將字符串構形成URL對象 s := "postgres://user:pass@host.com:5432/path?k=v#f" u, err := url.Parse(s) if err != nil { panic(err) } // 獲取schema部分 fmt.Println(u.Scheme) // User字段包含了Username和Password,須要分別獲取 fmt.Println(u.User) fmt.Println(u.User.Username()) p, _ := u.User.Password() fmt.Println(p) // Host字段包含了hostname和port fmt.Println(u.Host) fmt.Println(u.Hostname()) fmt.Println(u.Port()) // 取得Path和Fragment字段 fmt.Println(u.Path) fmt.Println(u.Fragment) // 取得query的key/value // 要取出各個key/value,使用ParseQuery()將RawQuery字段解析成map // key是字符串,value是字符串的slice,若是有key相同,則多個值放進這個slice fmt.Println(u.RawQuery) m, _ := url.ParseQuery(u.RawQuery) fmt.Println(m) fmt.Println(m["k"][0]) }
結果:orm
postgres user:pass user pass host.com:5432 host.com 5432 /path f k=v map[k:[v]] v
URL的Parse(string)
方法能夠將字符串構形成一個URL對象,URL的String()方法能夠返回編碼後的URL值。對象
例如:
urlstr := "http://www.cnblogs.com/f-ck-need-u" myurl,_ := url.Parse(urlstr) fmt.Println(myurl.String())
輸出:
http://www.cnblogs.com/f-ck-need-u
因爲URL的path和query部分可能包含特殊字符,直接使用純字符串構造URL會不安全。應該使用另外兩個函數將普通字符轉換成編碼後的字符:
func PathEscape(s string) string func QueryEscape(s string) string
再將編碼以後的path和query做爲Parse()方法的一部分。
例如:
package main import ( "fmt" "net/url" ) func main() { // 要構造:http://www.example.int/下 // Path: search // Query: food=pie aaa // action=like // 的URL s := "http://www.example.int" p := "search" path := url.PathEscape(p) // query部分 qfood := "pie aaa" qaction := "like" qqfood := url.QueryEscape(qfood) qqaction := url.QueryEscape(qaction) // 將query組合起來 query := "food=" + qqfood + "&action=" + qqaction // 構造url myurlstr := s + "/" + path + "?" + query myurl, err := url.Parse(myurlstr) if err != nil { panic(err) } // 解析URL fmt.Println(myurl.String()) // 解析url的query部分 fmt.Println(myurl.RawQuery) qmaps, _ := url.ParseQuery(myurl.RawQuery) fmt.Println(qmaps["food"]) fmt.Println(qmaps["action"]) }
這很麻煩,對於Query部分,更好的方法是使用Values.Encode()方法,見後文。
在URL結構中:有Path和RawPath兩個字段
type URL struct{ ... Path string // path (relative paths may omit leading slash) RawPath string // encoded path hint (see EscapedPath method) ... }
其中Path是解碼後的路徑,RawPath是編碼後的路徑。
前面解釋了PathEscape()函數,它是將字符串轉換爲編碼後的字符串,能夠直接將編碼後的結果做爲構造url的path部分,這樣是最安全的構造方式。
除此以外,還有一個EscapePath()方法:若是RawPath存在且有效,則直接返回RawPath字段的值,若是不存在,則EscapePath()本身計算一個編碼後的路徑。
URL.String()方法是直接調用EscapePath()來構造路徑部分的。
type Userinfo func User(username string) *Userinfo func UserPassword(username, password string) *Userinfo func (u *Userinfo) Password() (string, bool) func (u *Userinfo) String() string func (u *Userinfo) Username() string
User()函數構造一個Userinfo,但只包含Username不包含password。
UserPassword()函數構造一個Userinfo,包含Username和password,但由於明文顯示,不建議使用。
String()返回"username[:password]"格式的username和Password。
Username()和Password()分別返回對應的部分。
URL結構中有一個RawQuery字段:
type URL struct { ... RawQuery string ... }
RawQuery字段是編碼後的query。
有幾個方法:
func (u *URL) Query() Values
它讀取RawQuery字段的值並返回Values類型。但會直接丟棄錯誤或畸形的query部分,若是要檢查錯誤或畸形,使用ParseQuery()函數。
注意上面Query()的返回值是Values類型,它是一個map結構:
type Values map[string][]string func ParseQuery(query string) (Values, error) func (v Values) Add(key, value string) func (v Values) Del(key string) func (v Values) Encode() string func (v Values) Get(key string) string func (v Values) Set(key, value string)
ParseQuery()函數解析給定字符串query並將query的各個部分填充到返回值類型Values的map結構中,同時會檢查錯誤。
Add()、Del()、Set()和Get()都用來操做Values的map結構,意義都很清晰。惟一須要注意的是,Add()是追加操做,Set()是替換已有值,若是操做的key不存在,則直接建立。
Encode()是將Values中的數據根據key排序後編碼成URL的query,且是編碼後的query。
下面是一個用法:
package main import ( "fmt" "net/url" ) func main() { s := "http://www.example.int" p := "search" path := url.PathEscape(p) // query部分 values := url.Values{} values.Set("food","pie aaa") values.Add("action","like") values.Add("name","abc") values.Add("name","def") values.Add("name","gh") // Encode() == "action=like&food=pie+aaa&name=abc&name=def&name=gh" query := values.Encode() // 構造url myurlstr := s + "/" + path + "?" + query myurl, err := url.Parse(myurlstr) if err != nil { panic(err) } // 解析url fmt.Println(myurl.String()) }