TEACHING_GOPHER.png
大家好,我叫谢伟,是一名程序员。
近期会同步内置库的学习,主要参考文献官方文档和源代码
本节的主题:url
其实这是一个比较小的内置函数,主要用在网络请求方面上,可能最多的用途也就是用来处理网络请求的参数。当然如何你经常在项目中编写restfulAPI, 那么你也可能经常用到。
大纲:
原理知识
基本的用法
学习到了什么
1. 原理知识
URL: Uniform Resource Location, 称之为统一资源定位符。
出现的背景是:打个比方,比如你要找家里的东西,首先,你是不是会对东西的归属分析,比如,一把菜刀,你最大的可能是去厨房吧,这样能大概率的找到。网络上的资源也是这样,为了精准的找到服务器上的资源,有了 url。
那关于 url 有哪些知识呢?
代表的含义
组成部分:你要知道 url 的具体形式是什么吧
语法
1.1 代表的含义
就字面上的意思:统一资源定位符,唯一定位网络上的资源。
1.2 组成的部分
首先给个实例:
https://www.google.com
方案 scheme: 主要表示的是使用的是何种协议,比如 HTTP,FTP 等
服务器的地址: 你可以使用 ip 地址,也可以使用域名,所以IP 地址到域名之间存在一个映射关系
资源路径: 这部分就针对的是网络具体的资源的地址
这个好理解吧,和我们日常中的家庭地址、公司地址一样的含义,先定位省份,再定位市区,继而继续定位下去,直到找到你的地址。
网络的上资源,基本上还是借用这套思路:先定位到服务器上的地址,继而定位到具体的资源的地址。URL 就是这个意思。
1.3 语法组成
为了规范这些网络上的资源的地址,需要有一套规范,这套语法到底包含哪些东西?
方案: scheme ,具体指访问服务器上的资源使用的哪种协议
用户: 有些协议可以传入明文用户名和密码获取资源,比如 FTP
密码
主机: 服务器地址,可以是 IP 地址,也可以是域名信息
端口: 一串数字
路径: 资源的路径,使用 “/” 分隔
参数: example=one&hello=world 类似于这样的键值对
查询: 标识符是 “?” 和参数配合使用
片段: 标识符是 ”#“
好,不好理解,举个例子:
https://godoc.org/net/url#example-Values
scheme: https
用户: 无
密码: 无
主机: godoc.org
端口: 无
路径: net/url
参数: 无
查询: 无
片段: example-Values
有些是可选项,所以到最后,常用的是这几个概念:
scheme(方案、协议)
host(服务器地址)
port(服务器端口)
path(路径)
params(参数)
fragment(片段)
另外在请求的过程中还存在一个问题:编码,用来在URL 中表示各种不安全的字符
常见的编码:
字符 | 示例 |
---|---|
~ | %7 |
空格 | %20 |
% | %25 |
2. 基本用法
和根据上文的解释,我们明白 URL 的含义,但最终的它其实是一串字符串,只不过在网络资源请求层面,这串字符串赋予了更多的含义。
先撇开,官方的内置库的用法,我们首先想要自己实现,如何操作?
根据 url 的组成, 我们可能会设计如下面这个样子
type Url struct { Scheme string User string Password string Host string Port string Path string Params map[string][]string Fragment string}
好,假如设计成这样,我们将一串字符串转化成 我们定义的类型 Url, 如何得到各个部分呢?
https://godoc.org/net/url#example-Values
对照着各个含义,那么我们的思路应该是对这串字符的处理,比如按:,//,/,#
等划分得到我们需要的内容。
以上是我们自己的思考,如果感兴趣,可以自己单独实现下,想想:自己会提供哪些公开的方法?又会设计些什么辅助的功能?
下面查看官方的实现:
示例:
package mainimport ( "fmt" "net/url")var urlCollection struct { urlOne string urlTwo string urlThree string urlFour string urlFive string}func init() { urlCollection.urlOne = "https://www.google.com" urlCollection.urlTwo = "http://localhost:8887/v1/api/cloud_api/fetcher?email=1156143589@qq.com" // https://developer.readsense.cn/docs/retail/retailv2/regions.html#删除区域 urlCollection.urlThree = "https://developer.readsense.cn/docs/retail/retailv2/regions.html#%E5%88%A0%E9%99%A4%E5%8C%BA%E5%9F%9F" urlCollection.urlFour = "https://joe:joepassword@www.email.com/share_info.txt" urlCollection.urlFive = "https://godoc.org/net/url#example-Values"}func main() { OpUrl(urlCollection.urlOne) OpUrl(urlCollection.urlTwo) OpUrl(urlCollection.urlThree) OpUrl(urlCollection.urlFour) OpUrl(urlCollection.urlFive) }func OpUrl(urlString string) { URL, _ := url.Parse(urlString) fmt.Println("user", URL.User) fmt.Println("scheme", URL.Scheme) fmt.Println("host", URL.Host) fmt.Println("port", URL.Port()) fmt.Println("rawQuery", URL.RawQuery) fmt.Println("rawPath", URL.RawPath) fmt.Println("path", URL.Path) fmt.Println("forceQuery", URL.ForceQuery) fmt.Println("fragment", URL.Fragment) }
可以看出:url.Parse
可以将字符串转化成 URL 对象,该对象包含:User,Scheme,Host,Path,RawPath,ForceQuery,Fragment
字段和一些方法。
查看源代码,看URL 类型对象是如何定义?
type URL struct { Scheme stringOpaque string // encoded opaque dataUser *Userinfo // username and password informationHost string // host or host:portPath string // path (relative paths may omit leading slash)RawPath string // encoded path hint (see EscapedPath method)ForceQuery bool // append a query ('?') even if RawQuery is emptyRawQuery string // encoded query values, without '?'Fragment string // fragment for references, without '#'}
看上去,和我们预想的差别不大,但作者想的比我们深,比如把编码也考虑进去了,所有会有RawQuery,RawPath 等字段。
继续查看:
func PathEscape(s string) stringfunc PathUnescape(s string) (string, error)func QueryEscape(s string) stringfunc QueryUnescape(s string) (string, error)type Errorfunc (e *Error) Error() stringfunc (e *Error) Temporary() boolfunc (e *Error) Timeout() booltype EscapeErrorfunc (e EscapeError) Error() stringtype InvalidHostErrorfunc (e InvalidHostError) Error() stringtype URLfunc Parse(rawurl string) (*URL, error)func ParseRequestURI(rawurl string) (*URL, error)func (u *URL) EscapedPath() stringfunc (u *URL) Hostname() stringfunc (u *URL) IsAbs() boolfunc (u *URL) MarshalBinary() (text []byte, err error)func (u *URL) Parse(ref string) (*URL, error)func (u *URL) Port() stringfunc (u *URL) Query() Valuesfunc (u *URL) RequestURI() stringfunc (u *URL) ResolveReference(ref *URL) *URLfunc (u *URL) String() stringfunc (u *URL) UnmarshalBinary(text []byte) errortype Userinfofunc User(username string) *Userinfofunc UserPassword(username, password string) *Userinfofunc (u *Userinfo) Password() (string, bool)func (u *Userinfo) String() stringfunc (u *Userinfo) Username() stringtype Valuesfunc ParseQuery(query string) (Values, error)func (v Values) Add(key, value string)func (v Values) Del(key string)func (v Values) Encode() stringfunc (v Values) Get(key string) stringfunc (v Values) Set(key, value string)
可以看出,重要的用法有:
将字符串转化成 URL 对象,URL 对象获取相应的组成成分,存在相应的方法
URL 中的参数 Values 很重要,特别是我们编写 restfulAPI 的过程,也会思考这个问题,请求参数。 她的底层是
map[string][]string
, 所以可以Add, Del, Get,Set
等方法,这个东西需要记住,下次我们分析net/http
库的一个重要部分就是:对请求参数的处理
最后,再看下,这个库对错误的处理:
type EscapeError stringfunc (e EscapeError) Error() string { return "invalid URL escape " + strconv.Quote(string(e)) } type InvalidHostError stringfunc (e InvalidHostError) Error() string { return "invalid character " + strconv.Quote(string(e)) + " in host name"}
定义一个结构体
实现 Error 方法,继而实现 error 接口
3. 学到了什么
站在设计者的角度思考,我应该怎么设计?
如何设计的思路来源于原理,而不是胡乱思考。
返过来再去看书本中的原理
作者:谢小路
链接:https://www.jianshu.com/p/54b2639fb326