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:junchow520@hotmail.com"
u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", u.Scheme) //mailto
fmt.Printf("%v\n", u.Opaque) //junchow520@hotmail.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) string
- PathEscape将路径字符串转义,以便将其安全地放置在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%23footer
decodeurl, 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的逆转换
func QueryUnescape(s string) (string, error)
例如:
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][]string
func (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
有疑问加站长微信联系(非本文作者)