『Go 內置庫第一季:net/url』

TEACHING_GOPHER.png

你們好,我叫謝偉,是一名程序員。html

近期會同步內置庫的學習,主要參考文獻官方文檔和源代碼程序員

本節的主題:urljson

其實這是一個比較小的內置函數,主要用在網絡請求方面上,可能最多的用途也就是用來處理網絡請求的參數。固然如何你常常在項目中編寫restfulAPI, 那麼你也可能常常用到。後端

大綱:api

  • 原理知識
  • 基本的用法
  • 學習到了什麼

1. 原理知識

URL: Uniform Resource Location, 稱之爲統一資源定位符。安全

出現的背景是:打個比方,好比你要找家裏的東西,首先,你是否是會對東西的歸屬分析,好比,一把菜刀,你最大的多是去廚房吧,這樣能大機率的找到。網絡上的資源也是這樣,爲了精準的找到服務器上的資源,有了 url。bash

那關於 url 有哪些知識呢?服務器

  • 表明的含義
  • 組成部分:你要知道 url 的具體形式是什麼吧
  • 語法

1.1 表明的含義

就字面上的意思:統一資源定位符,惟必定位網絡上的資源。restful

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. 返過來再去看書本中的原理

<完>

相關文章
相關標籤/搜索