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

本文来自:简书

感谢作者:JunChow520

查看原文:GO URL

相关阅读 >>

nginx 服务搭建

Go操作mysql

手撸Golang Go与微服务 saga模式之7

Golang 主要做什么

Golang在linux系统上获取terminal终端的宽度

Go那些事儿|Go反射使用第二弹(valueof)

利用Go实现快看漫画网页版自动点击“下一话”,中间无需手动操作,但有一点疑惑,望大佬们解答。

Go fileserver

Golang 免费的吗?

手撸Golang Go与微服务 chatserver之2

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...