Go URL

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:[email protected]: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:[email protected]"
u, err := url.Parse(rawurl)
if err != nil {
    
    
    panic(err)
}

fmt.Printf("%v\n", u.Scheme) //mailto
fmt.Printf("%v\n", u.Opaque) //[email protected]

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()获取passwordpasswordSet字段值

func (u *Userinfo) Password() (string, bool) {
    
    
    if u == nil {
    
    
        return "", false
    }
    return u.password, u.passwordSet
}

例如:

rawurl := "postgres://user:[email protected]: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:[email protected]: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&notify_url=http%3A%2F%2F203.86.24.181%3A3000%2Falipay&out_trade_no=2201010101&sign_type=RSA2&timestamp=2017-11-10+17%3A54%3A34&version=1.0

おすすめ

転載: blog.csdn.net/weixin_43874301/article/details/121083162