Go 标准库net/url

URL 概述

import "net/url"

url 包解析 URL 并实现了查询的转码。

URL 提供了一种定位因特网上任意资源的手段,但这些资源是可以通过各种不同的方案(比如 HTTP 、 FTP 、 SMTP )来访问,因此 URL 语法会随着方案的不同而不同。

完整的 URL 格式为:

<schema>://<user>:<password>@<host>:<port>/<path>:<params>?<query>#<frag>

各部分字段说明:

  • scheme : 方案是如何访问指定资源的主要标识符,他会告诉负责解析 URL 应用程序应该使用什么协议;
  • user :用户名;
  • password :密码;
  • host : 主机组件标识了因特网上能够访问资源的宿主机器,可以有主机名或者是 IP 地址来表示;
  • port : 端口标识了服务器正在监听的网络端口。默认端口号是 80;
  • path : URL 的路径组件说明了资源位于服务器的什么地方;
  • params : URL 中通过协议参数来访问资源,比名值对列表,分号分割来进行访问;
  • query : 字符串是通过提问问题或进行查询来缩小请求资源类的范围;
  • frag : 为了引用部分资源或资源的一个片段,比如 URL 指定 HTML 文档中一个图片或一个小节;

HTTP 通常只处理整个对象,而不是对象的片段,客户端不能将片段传送给服务器。浏览器从服务器获取整个资源之后,会根据片段来显示你感兴趣的片段部分。

主要类型和方法

type URL

Go 中 URL 结构体如下:

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

示例代码

func main() {
	urlString := "https://admin:passwd@www.baidu.com:80/search?mq=test#12345"
	u, err := url.Parse(urlString)
	if err != nil {
		fmt.Println("parse error ", err)
	}
	fmt.Printf("u type is %T, u is %#v\n", u, u)
	/*
		u type is *url.URL,
		u is &url.URL{
			Scheme:"https", Opaque:"", User:(*url.Userinfo)(0xc000088150),
			Host:"www.baidu.com:80", Path:"/search", RawPath:"", ForceQuery:false,
			RawQuery:"mq=test", Fragment:"12345"
		}

	*/
	fmt.Printf("u.Scheme is %#v\n", u.Scheme) // u.Scheme is "https"
	fmt.Printf("u.Opaque is %#v\n", u.Opaque) // u.Opaque is ""
	fmt.Printf("u.User is %#v\n", u.User)
	// u.User is &url.Userinfo{username:"admin", password:"passwd", passwordSet:true}

	fmt.Printf("u.Host is %#v\n", u.Host)             // u.Host is "www.baidu.com:80"
	fmt.Printf("u.Path is %#v\n", u.Path)             // u.Path is "/search"
	fmt.Printf("u.RawPath is %#v\n", u.RawPath)       // u.RawPath is ""
	fmt.Printf("u.ForceQuery is %#v\n", u.ForceQuery) // u.ForceQuery is false
	fmt.Printf("u.RawQuery is %#v\n", u.RawQuery)     // u.RawQuery is "mq=test"
	fmt.Printf("u.Fragment is %#v\n", u.Fragment)     // u.Fragment is "12345"

}

或者可以利用 URL 结构体构建 url

u := &url.URL{
    Scheme: "https",
    Host:   "www.example.com",
    Path:   "/path",
    RawQuery: "param=value",
}
fmt.Println(u.String())    // 输出: https://www.example.com/path?param=value

func Parse

func Parse(rawurl string) (url *URL, err error)

Parse 方法将原始URL rawurl 解析为URL结构。该URL可以是相对路径,也可以是绝对路径

package main

import (
 "fmt"
 "net/url"
)

func main() {
 urlStr := "https://example.com/path?param1=value1&param2=value2"
 url, err := url.Parse(urlStr)
 if err != nil {
 fmt.Println("URL解析错误:", err)
 return
 }

 fmt.Println("URL方案:", url.Scheme)
 fmt.Println("主机:", url.Host)
 fmt.Println("路径:", url.Path)
 fmt.Println("查询字符串:", url.RawQuery)
}

结果

URL方案: https
主机: example.com
路径: /path
查询字符串: param1=value1&param2=value2

这里有个问题,如果 rawurl 是一个没有前面协议( http:// 或者 https:// )的字符串,则会报错

需要做一定的处理

```go
u:="127.0.0.1:8000"
// 先去除后缀
u1 := strings.TrimRight(u, "/")
// 然后判断前面是否有协议
if !strings.HasPrefix(u1, "http://") && !strings.HasPrefix(u1, "https://") {
	// 添加默认的http://作为协议前缀
	u1 = "http://" + strings.TrimSpace(u1)
}
// 然后再转换成URL结构体
parsedURL, err := url.Parse(u1)
if err != nil {
	c.log.L().Errorw("url转化失败", err)
	os.Exit(1)
}
```

func ParseRequestURI

func ParseRequestURI(rawURL string) (*URL, error)

ParseRequestURI 函数解析 rawurl 为一个 URL 结构体,本函数会假设 rawurl 是在一个 HTTP 请求里,因此会假设该参数是一个绝对 URL 或者绝对路径,并会假设该 URL 没有 #fragment 后缀。(网页浏览器会在去掉该后缀后才将网址发送到网页服务器)

func main() {
	urlString := "https://admin:passwd@www.baidu.com:80/search?mq=test#12345"
	u, err := url.ParseRequestURI(urlString)
	if err != nil {
		fmt.Println("parse error ", err)
	}
	fmt.Printf("u.Fragment is %#v\n", u.Fragment) // u.Fragment is ""

}

func (*URL) IsAbs

func (u *URL) IsAbs() bool

函数在 URL 是绝对 URL 时才返回真。

func (*URL) Query

func (u *URL) Query() Values

Query 方法解析 RawQuery 字段并返回其表示的 Values 类型键值对。

func (*URL) RequestURI

func (u *URL) RequestURI() string

RequestURI 方法返回编码好的 path?queryopaque?query 字符串,用在 HTTP 请求里。

func (*URL) String

func (u *URL) String() string

StringURL 重构为一个合法 URL 字符串。

func (*URL) Parse

func (u *URL) Parse(ref string) (*URL, error)

Parse 方法以 u 为上下文来解析一个 URLref 可以是绝对或相对 URL

本方法解析失败会返回 nil , err ;否则返回结果和 ResolveReference 一致。

func main() {
	u, _ := url.Parse("http://example.com/dir/")
	fmt.Println(u) // http://example.com/dir/

	result, _ := u.Parse("./search?mq=rabbitmq")
	fmt.Println(result) // http://example.com/dir/search?mq=rabbitmq
}

func (*URL) ResolveReference

func (u *URL) ResolveReference(ref *URL) *URL

本方法根据一个绝对 URI 将一个 URI 补全为一个绝对 URI

参数 ref 可以是绝对 URI 或者相对 URI

ResolveReference 总是返回一个新的 URL 实例,即使该实例和 u 或者 ref 完全一样。如果 ref 是绝对 URI ,本方法会忽略参照 URI 并返回 ref 的一个拷贝。

范例

func main() {
	urlString := "https://www.baidu.com/search?mq=rabbitmq&queue=people#12345"
	u, err := url.Parse(urlString)
	if err != nil {
		fmt.Println("parse error ", err)
	}
	fmt.Printf("u.IsAbs is %#v\n", u.IsAbs()) // u.IsAbs is true
	fmt.Printf("u.Query is %#v\n", u.Query())
	// u.Query is url.Values{"mq":[]string{"rabbitmq"}, "queue":[]string{"people"}}
	fmt.Printf("u.RequestURI is %#v\n", u.RequestURI())
	// u.RequestURI is "/search?mq=rabbitmq&queue=people"
	fmt.Printf("u.String is %#v\n", u.String())
	// u.String is "https://www.baidu.com/search?mq=rabbitmq&queue=people#12345"

}

type Values

type Values map[string][]string

Values 将建映射到值的列表。

它一般用于查询的参数和表单的属性。

不同于 http.Header 这个字典类型, Values 的键是大小写敏感的。

func ParseQuery

func ParseQuery(query string) (m Values, err error)

ParseQuery 函数解析一个 URL 编码的查询字符串,并返回可以表示该查询的 Values 类型的字典。

本函数总是返回一个包含了所有合法查询参数的非 nil 字典, err 用来描述解码时遇到的(如果有)第一个错误。

代码示例

func main() {
	rawUrl := "mq=rabbitmq&queue=people"
	v, err := url.ParseQuery(rawUrl)
	if err != nil {
		fmt.Println("ParseQuery error ", err)
	}
	fmt.Printf("v type is %T, v is %#v\n", v, v)
	// v type is url.Values, v is url.Values{"mq":[]string{"rabbitmq"}, "queue":[]string{"people"}}

	// 等价于下面的方法
	urlString := "https://www.baidu.com/search?mq=rabbitmq&queue=people#12345"
	u, _ := url.Parse(urlString)
	queryV := u.Query()
	fmt.Printf("queryV type is %T, queryV is %#v\n", queryV, queryV)
	// queryV type is url.Values, queryV is url.Values{"mq":[]string{"rabbitmq"}, "queue":[]string{"people"}}
}

func (Values) Get

func (v Values) Get(key string) string

Get 会获取 key 对应的值集的第一个值。

如果没有对应 key 的值集会返回空字符串。

获取值集请直接用 map 。

func (Values) Set

func (v Values) Set(key, value string)

Set 方法将 key 对应的值集设为只有 value ,它会替换掉已有的值集。

func (Values) Add

func (v Values) Add(key, value string)

Addvalue 添加到 key 关联的值集里原有的值的后面。

func (Values) Del

func (v Values) Del(key string)

Del 删除 key 关联的值集。

func (Values) Encode

func (v Values) Encode() string

Encode 方法将 v 编码为 ur 编码格式(“bar=baz&foo=quux”),编码时会以键进行排序。

范例

func main() {
	rawUrl := "mq=rabbitmq&queue=people"
	v, err := url.ParseQuery(rawUrl)
	if err != nil {
		fmt.Println("ParseQuery error ", err)
	}
	fmt.Printf("v type is %T, v is %#v\n", v, v)
	// v type is url.Values, v is url.Values{"mq":[]string{"rabbitmq"}, "queue":[]string{"people"}}

	fmt.Println(v.Get("mq")) // rabbitmq
	v.Set("mq", "redis")
	fmt.Println(v.Get("mq")) // redis
	v.Add("name", "wohu")
	fmt.Printf("v is %#v\n", v)
	// v is url.Values{"mq":[]string{"redis"}, "name":[]string{"wohu"}, "queue":[]string{"people"}}

	fmt.Printf("v.Encode is %#v\n", v.Encode()) // v.Encode is "mq=redis&name=wohu&queue=people"
	v.Del("name")
	fmt.Printf("v is %#v\n", v)
	// v is url.Values{"mq":[]string{"redis"}, "queue":[]string{"people"}}
}
package main
    import (
        "fmt"
        "net/url"
  
    )
    func main()  {
        v := url.Values{}
        //公共参数
        v.Add("Name", "wohu")
        v.Add("Age", "18")
        fmt.Println( v )
    }

查询转义

func QueryEscape

func QueryEscape(s string) string

QueryEscape 函数对 s 进行转码使之可以安全的用在 URL 查询里。

func QueryUnescape

func QueryUnescape(s string) (string, error)

QueryUnescape 函数用于将 QueryEscape 转码的字符串还原。

它会把 %AB 改为字节 0xAB ,将 + 改为空格 。

如果有某个 % 后面未跟两个十六进制数字,本函数会返回错误。

范例

func main() {
	rawUrl := "mq=rabbitmq&queue=people"
	stdUrl := url.QueryEscape(rawUrl)
	fmt.Printf("stdUrl is %v\n", stdUrl) // stdUrl is mq%3Drabbitmq%26queue%3Dpeople
	rawurl, _ := url.QueryUnescape(stdUrl)
	fmt.Printf("rawurl is %v\n", rawurl) // rawurl is mq=rabbitmq&queue=people
}
posted @ 2023-07-17 12:56  厚礼蝎  阅读(92)  评论(0编辑  收藏  举报