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())
}
相关文章
相关标签/搜索