golang中ServeMux解析


ServeMux解析

總覽

本來是想做一個UML出來讓這篇解析更清晰一點的,但是markdown的UML語法我一直搗鼓不出來。試了幾個軟件感覺也沒有
想象中的好用和方便,看來是時候自己開發一個了(笑).
口述一個流程,具體的函數大家可以頁內跳轉去看.
首先我們是通過ListenAndServe來監聽本地端口的,之后ListenAndServe將收到的新建一個Response連同收到的Request
作為參數調用ServeMux結構體ServeHTTP(省略了中間過程).ServeHTTP將
Request作為參數調用
Handler函數,Handler的返回值為一個Handler類型的接口,ServeHTTP會調用接口實現的ServeHTTP處理Response.

如果Request.URL.Path中有不合法的內容,則調用cleanPath清理,隨后將Request.Host以及清理后的
內容傳入handler函數,隨后返回一個RedirectHandler以及handler所返回的路徑。如果
Request.URL.Path合法,那么
直接調用handler,返回值與handler返回值相同。

handler中通過判斷ServeMux.hosts來決定是否實現pattern = r.Host + r.URL.Path.之后將pattern作為參數調用match,並將
match的返回值返回.

match的判別方式比較"有趣",它雖然沒實現為樹形結構(只是用了映射),但是搜索的方法就是樹形,因為URL路徑就是個樹形.它按照樹的根節點
與子節點的關系進行判斷,譬如路徑"/home/select/usercourse",match在匹配的時候會首先匹配到"/"(假如我們注冊了),其次是"/home",
之后逐層匹配下來,假如我們沒注冊過"/home/select/usercourse",但是注冊了"/home/select/",那么match就會匹配到這一層.然后返回
"/home/select/"的Handler以及url(pattern).match函數的匹配規則實現在pathMatch

ServeMux結構體

type ServeMux struct {
		mu    sync.RWMutex
		m     map[string]muxEntry
		hosts bool // whether any patterns contain hostnames
	}

type muxEntry struct {
    	explicit bool
    	h        Handler
    	pattern  string
    }

這樣看起來還蠻直觀的,mu是一個互斥鎖,m則是我們所要用的路由(router),其中的鍵(key)則是我們掛載的路徑,
對應的值則是響應的muxEntry,hosts指明了我們是否在每個路徑中都聲明了hostnames.比如我們平常用'/'來表示
掛載在域名根目錄下的HandlerFunc,但是如果hosts=true,那么我們就需要'127.0.0.1/'來做同樣的事情了.

下面我們來看muxEntry,h比較明確,就是我們注冊的處理過程.pattern與ServeMux中m的key相同,也就是說是我們
注冊的路徑,而explicit的用途其實是比較隱晦的。程序會根據explicit判別這個路徑的Handler是否是用戶注冊的。
如果是程序為了Redirect(詳細點擊這里)而設定的,那么在它是可以被覆蓋的,否則就是不可以被覆蓋的.

NewServeMux()

DefaultServeMux變量就是直接調用的這個函數。那么這個函數只make了一個變量m,也就是為map分配了空間。
在golang的語法中,結構體里未聲明的變量也是存在的(即分配了內存),只不過是該類型的默認值,比如hosts就
被設置成了false.這個問題不過多解釋,有興趣的話可以看一下golang中的相關內容

pathMatch()

這個函數是ServeMux用來匹配路徑的主要函數,所以看一下策略還是很重要的.
函數中的參數pattern是我們注冊的路徑,path是用戶請求的路徑

func pathMatch(pattern, path string) bool {
	if len(pattern) == 0 {
		// should not happen
		return false
	}
	n := len(pattern)
	if pattern[n-1] != '/' {
		return pattern == path
	}
	return len(path) >= n && path[0:n] == pattern
}

如果我們掛載的路徑不是以'/'結尾的,那么就直接判斷兩個參數是否相同。如果是以'/'結尾的,只要path的路徑包含
pattern那么就被判定是匹配。也就是說,如果我注冊了/home/select/,那么/home/select/hello也會被定位到/home/select/
掛載的HandlerFunc上.這樣做相當於為路徑設置了一個index,不符合規則的URL都會被Redirect到這個index上

* ServeMux.Handler()

注釋寫的很清楚,這個函數就是處理URL,然后調用*ServeMux.handler().首先調用cleanPath清理請求URL中的不合法內容。如果存在不合法內容,
則將清理過的URL交由*ServeMux.handler()處理並獲得匹配到的pattern,然后修改url.Path的內容並調用RedirectHandler.
如果內容合法,則直接調用*ServeMux.handler()並返回結果

* ServeMux.handler()

調用ServeMux.match()(封裝了pathMatch函數)來獲得匹配到的Handler以及對應pattern,如果ServeMux.hosts==true,那么
傳入的參數為host + path,如果找不到的話,調用NotFoundHandler函數,並將其結果返回.

* ServeMux.Handle()

Handle函數是用來注冊路徑與處理過程的.如果該路徑已經存在了一個用戶注冊的Handler則會panic(意思就是說不支持覆蓋).判別了合法參數以后就將
pattern作為key,新建一個muxEntry類型變量作為value加入到map中。

if pattern[0] != '/' {
	mux.hosts = true
}

這是這個函數中比較有意思的一個部分,通過這里我們可以看到如果注冊路徑的時候並不是以'/'開頭的,那么ServeMux就會開啟hosts,然后會在
請求到達的時候將URL.Host和URL.Path連接在一起放入match中尋找,具體信息請看這里

接下來是關於路徑的處理,也就是關於"/home"與"/home/"的區別.我們先來看看作者怎么說

	// Helpful behavior:
	// If pattern is /tree/, insert an implicit permanent redirect for /tree.
	// It can be overridden by an explicit registration.

如果路徑的末尾是以'/'結尾並且該路徑去掉末尾的'/'以后並沒有被注冊.那么將會去掉'/'並且為其綁定一個Redirect到現在的路徑.
我自己寫起來都覺得繞,舉個例子就清楚了.
我注冊了一個路徑"/home/",但是沒有注冊"/home",那么如果用戶訪問了"/home"會發生什么呢?是的,會被Redirect到"/home/".
需要注意的是,這里的muxEntry中的explicit沒有填,也就是說是false,那么即是可以覆蓋的.

* ServeMux.ServeHTTP()

ServeHTTP會檢測非法的URI(* )
如果通過檢測就會調用自身的Handler()來返回注冊的Handler,隨后調用Handler的ServeHTTP方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM