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,一般格式为:
[scheme:][//[userinfo@]host][/]path[?query][#fragment]
由于path和query部分只能使用大小写字母、数字以及有限的几个特殊标点,其它所有的字符都需要进行URL编码:百分号+2位16进制数。例如,空格被编码为"%20",斜线被编码为"%2f",有时候query的value中空格会被编码为"+"。
例如,query部分被编码后的内容如下:
name=Hiram%20Veeblefeetzer&age=35&country=Madagascar+abc
它表示3个key/value:name="Hiram Veeblefeetzer"
、age=35
、country=Madagascar abc
。
关于URL,其中:
- Host字段是包含host和port两部分的,如果需要分别返回host、port,使用
URL.Hostname
、URL.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对象,并解析其中的各个部分:
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])
}
结果:
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())
}