Go Web:URLs

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=35country=Madagascar abc編碼

關於URL,其中:url

  • Host字段是包含host和port兩部分的,若是須要分別返回host、port,使用URL.HostnameURL.Port
  • User字段包含了Username和Password,要分別返回它們,使用URL.User.Username()URL.User.Password()
  • Path字段表示解碼後的URL路徑,也就是不帶"%"的普通字符串路徑
  • RawPaht字段表示編碼後的安全路徑,即帶上了"%"的路徑
  • RawQuery字段表示編碼後的Query,即打上了"%"的query字符串,它包含了全部key/value,要分離每一個key/value,使用ParseQuery()方法將它們解析到一個map中

URL解析示例

使用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

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部分

在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()來構造路徑部分的。

Userinfo

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()分別返回對應的部分。

Query部分

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())
}
相關文章
相關標籤/搜索