URL简介
URL全称Uniform Resource Location统一资源定位符,用于定位Internet中待访问的文档或资源。
URL提供了一种定位Internet上任意资源的手段,资源可通过各种不同的方案(比如HTTP、FTP、SMTP)来访问,因此URL语法随着方案不同而不同。
<schema>://<user>:<password>@<host>:<port>/<path>:<params>?<query>#<frag>URL格式
scheme://[userinfo@]host[:port]/path[?query][#fragment]scheme://[userinfo]@[host]:[port]/path?key1=value1&key2=value2#fragment
格式
描述
scheme
方案/协议,比如http、https、ftp、file…
host
主机名或IP地址,用于定位网络位置
port
服务端口
path
主机上的资源路径
query
查询字符串,使用&连接符链接的键值对。
fragment
分段字段
URL编码
- URL不能有非ASCII字符,非ASCII使用%后跟两位16进制数表示。
- URL中不能存在空格,空格使用+表示。
字符
编码
~
%7
Space
%20
%
%25
net/url
net包对于网络I/O提供了便携式接口,包括TCP/IP、UDP、域名解析、UNIX Socket。尽管net包提供了大量访问底层的接口,但大多数情况下,客户端仅仅只需要最基本的接口。
Golang标准库net/url包
- 可用于获取URL属性
- 解析URL并实现查询转义
URL解读
URL结构体
type URL struct {Scheme string
Opaque string // 编码后的不透明数据
User *Userinfo // 用户名和密码信息
Host string // 主机地址 或 主机:端口
Path string // path (relative paths may omit leading slash)
RawPath string // 详细资源地址
ForceQuery bool // append a query ('?') even if RawQuery is empty
RawQuery string // 已编码查询字符串
Fragment string // 引用片段可用于表示文档位置
RawFragment string // encoded fragment hint (see EscapedFragment method)
}
url.Parse
函数签名
func Parse(rawurl string) (*URL, error)- 将原生的rawurl字符串解析转换为URL结构体,进而获取URL的相关属性。
- 原生URL可以是绝对地址,也可以是相对地址。
例如:
rawurl := "postgres://user:pass@host.com:5432/path1/path2/path3?k=v&k1=v1&k2=v2#f"u, err := url.Parse(rawurl)
if err != nil {
t.Fatal(err)
}
fmt.Printf("%v\n", u.Scheme) //postgres
fmt.Printf("%v\n", u.Opaque)
fmt.Printf("%v\n", u.User) //user:pass
fmt.Printf("%v\n", u.Host) //host.com:5432
fmt.Printf("%v\n", u.Path) // /path1/path2/path3
fmt.Printf("%v\n", u.RawPath)
fmt.Printf("%v\n", u.ForceQuery) //false
fmt.Printf("%v\n", u.RawQuery) //k=v&k1=v1&k2=v2
fmt.Printf("%v\n", u.Fragment) //f
fmt.Printf("%v\n", u.RawFragment)
URL.Opaque
url.Opaque表示透明类型的URL,即Scheme后没有//的URL。
rawurl := "mailto:coding@gmail.com"u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", u.Scheme) //mailto
fmt.Printf("%v\n", u.Opaque) //coding@gmail.com
URL.User
URL结构体中User字段包含了所有的认证信息url.Userinfo
type Userinfo struct {username string
password string
passwordSet bool
}
通过Userinfo.Username()方法获取username字段值
func (u *Userinfo) Username() string {if u == nil {
return ""
}
return u.username
}
通过Userinfo.Password()获取password和passwordSet字段值
func (u *Userinfo) Password() (string, bool) {if u == nil {
return "", false
}
return u.password, u.passwordSet
}
例如:
rawurl := "postgres://user:pass@host.com:5432/path1/path2/path3?k=v&k1=v1&k2=v2#f"u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
userinfo := u.User
username := userinfo.Username()
fmt.Printf("%v\n", username) //user
password, ok := userinfo.Password()
if ok {
fmt.Printf("%v\n", password) //pass
}
URL.Host
url.Host同时包含主机名和端口,若端口存在则需要分割后才能提取。
例如:
rawurl := "postgres://user:pass@host.com:5432/path1/path2/path3?k=v&k1=v1&k2=v2#f"u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
host := u.Host
fmt.Printf("%v\n", host) //host.com:5432
//判断是否存在:
ok := strings.Contains(host, ":")
if ok {
h := strings.Split(host, ":")
fmt.Printf("%v\n", h) //[host.com 5432]
addr := h[0]
port := h[1]
fmt.Printf("%v\n", addr) //host.com
fmt.Printf("%v\n", port) //5432
}
url.ParseRequestURI
url.ParseRequestURI解析原生URL为一个URL结构体
url.ParseRequestURI原生URL是一个绝对URL或绝对路径
url.ParseRequestURI会忽略URL中的fragment分片参数
例如:
rawurl := "http://www.baidu.com/search?q=dotnet"u, err := url.ParseRequestURI(rawurl)
if err != nil {
t.Fatal(err)
}
t.Logf("url.Scheme: %v\f", u.Scheme) //url.Scheme: http
t.Logf("url.Host: %v\f", u.Host) //url.Host: www.baidu.com
t.Logf("url.Path: %v\f", u.Path) //url.Path: /search
t.Logf("url.RawQuery: %v\f", u.RawQuery) //url.RawQuery: q=dotnet
url.PathEscape
URL中不允许存在空格和非ASCII字符,如果URL的资源路径path字符串中存在空格和ASCII字符则可采用url.PathEscape将非ASCII字符转义为使用%后跟2位16进制数的形式,采用PathUnescape方法解码还原为原始模式。
func PathEscape(s string) stringPathEscape将路径字符串转义,以便将其安全地放置在URL路径段中。
func PathUnescape(s string) (string, error)- PathUnescape执行与PathEscape的逆转换
- PathUnescape和QueryUnescape相同,只是PathUnescape不会将+加号转换为空格。
例如:
rawurl := "http://www.baidu.com/search?id=1&name=管 理 员&pid=0#footer"encodeurl := url.PathEscape(rawurl)
t.Log(encodeurl)http:%2F%2Fwww.baidu.com%2Fsearch%3Fid=1&name=%E7%AE%A1%20%E7%90%86%20%E5%91%98&pid=0%23footerdecodeurl, err := url.PathUnescape(encodeurl)
if err != nil {
t.Fatal(err)
}
t.Log(decodeurl)http://www.baidu.com/search?id=1&name=管 理 员&pid=0#footer
转义前
转义后
/
%2F
?
%3F
#
%23
空格
%20
- PathEscape会将空格转换为%20
- PathEscape不会对URL中的:和&进行转义编码
url.QueryEscape
- 对URL的查询字符串进行编码和解码
- QueryEscape将URL中的查询字符串转义,以便安全地放置到URL查询字符串中。
URL中不允许存在空格和非ASCII字符,如果URL的查询字符串query字符串中存在空格和ASCII字符则可采用url.QueryEscape将非ASCII字符转化为使用%后跟2位16进制数的形式,采用QueryUnescape方法解码还原为原始模式。
func QueryEscape(s string) string例如:
rawurl := "http://www.baidu.com/search?id=1&name=管 理 员&pid=0#footer"encodeurl := url.QueryEscape(rawurl)
t.Log(encodeurl)http%3A%2F%2Fwww.baidu.com%2Fsearch%3Fid%3D1%26name%3D%E7%AE%A1+%E7%90%86+%E5%91%98%26pid%3D0%23footer
- QueryUnescape执行与QueryEscape的逆转换
例如:
rawurl := "http://www.baidu.com/search?id=1&name=管 理 员&pid=0#footer"encodeurl := url.QueryEscape(rawurl)
decodeurl, err := url.QueryUnescape(encodeurl)
if err != nil {
t.Fatal(err)
}
t.Log(decodeurl)http://www.baidu.com/search?id=1&name=管 理 员&pid=0#footer
转义前
转义后
:
%3A
/
%2F
?
%3F
&
%26
#
%23
空格
+
- QueryEscape会将空格转换为+加号
- QueryEscape会将/路径分隔符转换为%2F
url.ParseQuery
func ParseQuery(query string) (Values, error) {m := make(Values)
err := parseQuery(m, query)
return m, err
}
URL中的查询字符串可采用url.ParseQuery解析生成一个url.Values字典实例。
例如:
rawurl := "http://www.baidu.com/search?id=1&name=admin&pid=0#footer"u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
query := u.RawQuery
fmt.Printf("%v\n", query) //id=1&name=admin&pid=0
values, err := url.ParseQuery(query)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", values) //map[id:[1] name:[admin] pid:[0]]
fmt.Printf("%v\n", values["id"][0]) //1
fmt.Printf("%v\n", values["name"][0]) //admin
fmt.Printf("%v\n", values["pid"][0]) //0
封装:从URL的查询字符串中通过键获得值
func GetQueryStringField(rawurl string, key string) (string, error) {u, err := url.Parse(rawurl)
if err != nil {
return "", err
}
query := u.RawQuery
values, err := url.ParseQuery(query)
if err != nil {
return "", err
}
value := values.Get(key)
return value, nil
}rawurl := "http://www.baidu.com/search?id=1&name=admin&pid=0#footer"
id, err := GetQueryStringField(rawurl, "id")
if err != nil {
panic(err)
}
fmt.Printf("%v\n", id) //1
url.Query
func (u *URL) Query() Values {v, _ := ParseQuery(u.RawQuery)
return v
}rawurl := "http://www.baidu.com/search?id=1&name=admin&pid=0#footer"
u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
values := u.Query()
fmt.Printf("%v\n", values) //map[id:[1] name:[admin] pid:[0]]
id := values.Get("id")
fmt.Printf("%v\n", id) //1
重置查询字符串键值对
rawurl := "http://www.baidu.com/search?id=1&name=admin&pid=0#footer"u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
values := u.Query()
values.Set("id", "100")
u.RawQuery = values.Encode()
t.Log(u) //http://www.baidu.com/search?id=100&name=admin&pid=0#footer
url.Values
type Values map[string][]stringfunc (v Values) Get(key string) string
func (v Values) Set(key, value string)
func (v Values) Add(key, value string)
func (v Values) Del(key string)
func (v Values) Encode() string
将url.Values转换为查询字符串
func BuildHTTPQueryString(values url.Values) string {var keys []string
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)
bb := bytes.Buffer{}
for _, key := range keys {
val := values.Get(key)
bb.WriteString(key)
bb.WriteString("=")
bb.WriteString(val)
bb.WriteString("&")
}
return strings.TrimRight(bb.String(), "&")
}values := url.Values{}
values.Add("id", "1")
values.Add("name", "admin")
values.Add("pid", "0")
queryString := BuildHTTPQueryString(values)
fmt.Printf("%v\n", queryString) //id=1&name=admin&pid=0
values.Encode
例如:HTTP请求将URL传递过来的参数编码为以&连接的字符串
values := url.Values{}values.Add("app_id", "2016073100129537")
values.Add("out_trade_no", "2201010101")
values.Add("method", "alipay.trade.app.pay")
values.Add("notify_url", "http://203.86.24.181:3000/alipay")
values.Add("timestamp", "2017-11-10 17:54:34")
values.Add("sign_type", "RSA2")
values.Add("version", "1.0")
encode := values.Encode()
fmt.Printf("%v\n", encode)app_id=2016073100129537&method=alipay.trade.app.pay¬ify_url=http%3A%2F%2F203.86.24.181%3A3000%2Falipay&out_trade_no=2201010101&sign_type=RSA2×tamp=2017-11-10+17%3A54%3A34&version=1.0