『Go 内置库第一季:net/url』

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 main

import (
    "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     string
Opaque     string    // encoded opaque data
User       *Userinfo // username and password information
Host       string    // host or host:port
Path       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 empty
RawQuery   string    // encoded query values, without '?'
Fragment   string    // fragment for references, without '#'
}

看上去,和我们预想的差别不大,但作者想的比我们深,比如把编码也考虑进去了,所有会有RawQuery,RawPath 等字段。

继续查看:

func PathEscape(s string) string
func PathUnescape(s string) (string, error)
func QueryEscape(s string) string
func QueryUnescape(s string) (string, error)
type Error
func (e *Error) Error() string
func (e *Error) Temporary() bool
func (e *Error) Timeout() bool
type EscapeError
func (e EscapeError) Error() string
type InvalidHostError
func (e InvalidHostError) Error() string
type URL
func Parse(rawurl string) (*URL, error)
func ParseRequestURI(rawurl string) (*URL, error)
func (u *URL) EscapedPath() string
func (u *URL) Hostname() string
func (u *URL) IsAbs() bool
func (u *URL) MarshalBinary() (text []byte, err error)
func (u *URL) Parse(ref string) (*URL, error)
func (u *URL) Port() string
func (u *URL) Query() Values
func (u *URL) RequestURI() string
func (u *URL) ResolveReference(ref *URL) *URL
func (u *URL) String() string
func (u *URL) UnmarshalBinary(text []byte) error
type Userinfo
func User(username string) *Userinfo
func UserPassword(username, password string) *Userinfo
func (u *Userinfo) Password() (string, bool)
func (u *Userinfo) String() string
func (u *Userinfo) Username() string
type Values
func ParseQuery(query string) (Values, error)
func (v Values) Add(key, value string)
func (v Values) Del(key string)
func (v Values) Encode() string
func (v Values) Get(key string) string
func (v Values) Set(key, value string)

可以看出,重要的用法有:

  • 将字符串转化成 URL 对象,URL 对象获取相应的组成成分,存在相应的方法
  • URL 中的参数 Values 很重要,特别是我们编写 restfulAPI 的过程,也会思考这个问题,请求参数。 她的底层是 map[string][]string , 所以可以Add, Del, Get,Set等方法,这个东西需要记住,下次我们分析 net/http 库的一个重要部分就是:对请求参数的处理

最后,再看下,这个库对错误的处理:


type EscapeError string

func (e EscapeError) Error() string {
    return "invalid URL escape " + strconv.Quote(string(e))
}

type InvalidHostError string

func (e InvalidHostError) Error() string {
    return "invalid character " + strconv.Quote(string(e)) + " in host name"
}
  • 定义一个结构体
  • 实现 Error 方法,继而实现 error 接口

3. 学到了什么

  1. 站在设计者的角度思考,我应该怎么设计?
  2. 如何设计的思路来源于原理,而不是胡乱思考。
  3. 返过来再去看书本中的原理

<完>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容