Go 标准库之 net.url

Go 内置的 net/url 包提供了用于 URL 解析、构建和查询的功能。这个包使我们能够处理 URL,从中提取出各个部分,比如协议、主机、路径和查询参数等。

https://pkg.go.dev/net/url

简介

格式

1
2
3
<schema>://<user>:<password>@<host>:<port>/<path>:<params>?<query>#<frag>
或者
<scheme>:<opaque>[?<query>][#<fragment>]
  • scheme: 方案是如何访问指定资源的主要标识符,他会告诉负责解析URL应用程序应该使用什么协议。

  • host: 主机组件标识了因特网上能够访问资源的宿主机器,可以有主机名或者是IP地址来表示。

  • port: 端口标识了服务器正在监听的网络端口。默认端口号是80。

  • path: URL的路径组件说明了资源位于服务器的什么地方。

  • params: URL中通过协议参数来访问资源,比名值对列表,分号分割来进行访问。

  • query: 字符串是通过提问问题或进行查询来缩小请求资源类的范围。frag: 为了引用部分资源或资源的一个片段,比如URL指定HTML文档中一个图片或一个小节。HTTP通常只处理整个对象,而不是对象的片段,客户端不能将片段传送给服务器。浏览器从服务器获取整个资源之后,会根据片段来显示你感兴趣的片段部分。

概览

URL 结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
type URL struct {
Scheme string // 具体指访问服务器上的资源使用的哪种协议
Opaque string // 编码后的不透明数据
User *Userinfo // 户名和密码信息,有些协议需要传入明文用户名和密码来获取资源,比如 FTP
Host string // host或host:port,服务器地址,可以是 IP 地址,也可以是域名信息
Path string // 路径,使用"/"分隔
RawPath string // 已编码的路径提示(参见EscapedPath方法)
OmitHost bool // do not emit empty host (authority)
ForceQuery bool // 添加一个查询('?'),即使RawQuery为空
RawQuery string // 编码后的查询字符串,没有'?'
Fragment string // 引用的片段(文档位置),没有'#'
RawFragment string // 编码后的引用的片段
}

一些常用的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func QueryEscape(s string) string                       ueryEscape 函数对 s 进行转码使之可以安全的用在 URL 查询里。


func QueryUnescape(s string) (string, error) QueryUnescape 函数用于将 QueryEscape 转码的字符串还原。它会把 %AB 改为字节 0xAB,将'+' 改为' '。如果有某个 % 后面未跟两个十六进制数字,本函数会返回错误。


type Error struct { Op string URL string Err error } Error 会报告一个错误,以及导致该错误发生的 URL 和操作。
func (e *Error) Error() string


type EscapeError string
func (e EscapeError) Error() string


type URL struct {} URL 类型代表一个解析后的 URL(或者说,一个 URL 参照)。URL 基本格式如下:

func Parse(rawurl string) (url *URL, err error) Parse 函数解析 rawurl 为一个 URL 结构体,rawurl 可以是绝对地址,也可以是相对地址。
func ParseRequestURI(rawurl string) (url *URL, err error) ParseRequestURI 函数解析 rawurl 为一个 URL 结构体,本函数会假设 rawurl 是在一个 HTTP 请求里,因此会假设该参数是一个绝对 URL 或者绝对路径,并会假设该 URL 没有#fragment 后缀。(网页浏览器会在去掉该后缀后才将网址发送到网页服务器)
func (u *URL) IsAbs() bool 判断是否是绝对路径func (*URL) IsAbs,在 URL 是绝对 URL 时才返回真。
func (u *URL) Query() Values 解析 RawQuery 字段并返回其表示的 Values 类型键值对。
func (u *URL) RequestURI() string 返回编码好的 path?queryopaque?query 字符串,用在 HTTP 请求里。
func (u *URL) String() stringURL 重构为一个合法 URL 字符串。
func (u *URL) Parse(ref string) (*URL, error)u 为上下文来解析一个 URLref 可以是绝对或相对 URL。本方法解析失败会返回 nil, err;否则返回结果和 ResolveReference 一致。
func (u *URL) ResolveReference(ref *URL) *URL 根据一个绝对 URI 将一个 URI 补全为一个绝对 URI,参见 RFC 3986 节 5.2。参数 ref 可以是绝对 URI 或者相对 URIResolveReference 总是返回一个新的 URL 实例,即使该实例和 u 或者 ref 完全一样。如果 ref 是绝对 URI,本方法会忽略参照 URI 并返回 ref 的一个拷贝。


type Userinfo struct { // 内含隐藏或非导出字段 } Userinfo 类型是一个 URL 的用户名和密码细节的一个不可修改的封装。一个真实存在的 Userinfo 值必须保证有用户名(但根据 RFC 2396 可以是空字符串)以及一个可选的密码。

func User(username string) *Userinfo User 函数返回一个用户名设置为 username 的不设置密码的 *Userinfo
func UserPassword(username, password string) *Userinfo UserPassword 函数返回一个用户名设置为 username、密码设置为 password 的 *Userinfo。这个函数应该只用于老式的站点,因为风险很大,不建议使用,参见 RFC 2396。
func (u *Userinfo) Username() string Username 方法返回用户名。
func (u *Userinfo) Password() (string, bool) 如果设置了密码返回密码和真,否则会返回假。
func (u *Userinfo) String() string String 方法返回编码后的用户信息,格式为 "username[:password]"。


type Values map[string][]string Values 将建映射到值的列表。它一般用于查询的参数和表单的属性。不同于 http.Header 这个字典类型,Values 的键是大小写敏感的。

func ParseQuery(query string) (m Values, err error) ParseQuery 函数解析一个 URL 编码的查询字符串,并返回可以表示该查询的 Values 类型的字典。本函数总是返回一个包含了所有合法查询参数的非 nil 字典,err 用来描述解码时遇到的(如果有)第一个错误。
func (v Values) Get(key string) string Get 会获取 key 对应的值集的第一个值。如果没有对应 key 的值集会返回空字符串。获取值集请直接用 map
func (v Values) Set(key, value string) Set 方法将 key 对应的值集设为只有 value,它会替换掉已有的值集。
func (v Values) Add(key, value string) Addvalue 添加到 key 关联的值集里原有的值的后面。
func (v Values) Del(key string) Del 删除 key 关联的值集。
func (v Values) Encode() string Encode 方法将 v 编码为 url 编码格式 ("bar=baz&foo=quux"),编码时会以键进行排序。

示例

解析普通 URL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
"fmt"
"net/url"
)

func main() {
// 定义一个 URL 字符串
rawURL := "https://www.example.com/path?query=123#fragment"

// 解析 URL
parsedURL, err := url.Parse(rawURL)
if err != nil {
fmt.Println("Error parsing URL:", err)
return
}

// 获取 URL 的各个组成部分
fmt.Println("Scheme:", parsedURL.Scheme) // 输出协议部分,如 "https"
fmt.Println("Host:", parsedURL.Host) // 输出主机部分,如 "www.example.com"
fmt.Println("Path:", parsedURL.Path) // 输出路径部分,如 "/path"
fmt.Println("RawQuery:", parsedURL.RawQuery) // 输出原始查询字符串,如 "query=123"
fmt.Println("Fragment:", parsedURL.Fragment) // 输出锚点部分,如 "fragment"
}

再来一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
s:="mysql+pymysql://base:xxxx.mysql.rds.aliyuncs.com:3306/dbtest?charset=utf8&test=test#12345"
u,err:=url.Parse(s)
if err!=nil{
panic(err)
}
fmt.Println(reflect.TypeOf(u)) //*url.URL

fmt.Println(u.Opaque)
fmt.Println(u.ForceQuery) //false

//user 包含了所有的验证信息
fmt.Println(u.User) // base:base-testdempo123
fmt.Println(u.User.String()) //base:base-testdempo123
fmt.Println(u.User.Username()) //base
fmt.Println(u.User.Password()) //base-testdempo123 true

// 解析后的Scheme
fmt.Println(u.Scheme) //mysql+pymysql
//解析出路径
fmt.Println(u.Path) // /dbtest
fmt.Println(u.Fragment) //12345
fmt.Println(u.RawPath)

//host 包含了主机名和端口
fmt.Println(u.Host) //rr-2zeqddi95ss6s26cddz62o.mysql.rds.aliyuncs.com:3306
fmt.Println(u.RawQuery) //charset=utf8&test=test

解析查询字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"
"net/url"
)

func main() {
// 定义一个查询字符串
queryString := "name=John&age=30&city=New+York"

// 解析查询字符串
queryParams, err := url.ParseQuery(queryString)
if err != nil {
fmt.Println("Error parsing query string:", err)
return
}

// 获取查询参数的值
fmt.Println("Name:", queryParams.Get("name")) // 输出 "John"
fmt.Println("Age:", queryParams.Get("age")) // 输出 "30"
fmt.Println("City:", queryParams.Get("city")) // 输出 "New York"

// 遍历所有查询参数
for key, values := range queryParams {
fmt.Println(key, ":", values)
}
}

构造 URL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"net/url"
)

func main() {
// 构造 URL 的各个组成部分
scheme := "https"
host := "www.example.com"
path := "/path"
query := url.Values{}
query.Add("query", "123")
fragment := "fragment"

// 构造 URL
rawURL := fmt.Sprintf("%s://%s%s?%s#%s", scheme, host, path, query.Encode(), fragment)

fmt.Println("Constructed URL:", rawURL) // 输出 "https://www.example.com/path?query=123#fragment"
}

注意事项

  1. URL 编码:在处理 URL 时,特别是查询字符串中的参数值,需要注意 URL 编码。net/url 包提供了 url.QueryEscapeurl.QueryUnescape 函数来对字符串进行编码和解码。

  2. 错误处理:在解析 URL 或查询字符串时,可能会出现错误(如格式不正确等),需要检查返回的错误并进行适当的处理。


Go 标准库之 net.url
https://flepeng.github.io/021-Go-32-Go-标准库-Go-标准库之-net-url/
作者
Lepeng
发布于
2024年12月3日
许可协议