原本是想作一個UML出來讓這篇解析更清晰一點的,可是markdown的UML語法我一直搗鼓不出來。試了幾個軟件感受也沒有
想象中的好用和方便,看來是時候本身開發一個了(笑).
口述一個流程,具體的函數你們能夠頁內跳轉去看.
首先咱們是經過ListenAndServe來監聽本地端口的,以後ListenAndServe將收到的新建一個Response連同收到的Request
做爲參數調用ServeMux結構體的ServeHTTP(省略了中間過程).ServeHTTP將Request做爲參數調用
Handler函數,Handler的返回值爲一個Handler類型的接口,ServeHTTP會調用接口實現的ServeHTTP處理Response.golang
若是Request.URL.Path中有不合法的內容,則調用cleanPath清理,隨後將Request.Host以及清理後的
內容傳入handler函數,隨後返回一個RedirectHandler以及handler所返回的路徑。若是Request.URL.Path合法,那麼
直接調用handler,返回值與handler返回值相同。markdown
handler中經過判斷ServeMux.hosts來決定是否實現pattern = r.Host + r.URL.Path.以後將pattern做爲參數調用match,並將
match的返回值返回.app
match的判別方式比較"有趣",它雖然沒實現爲樹形結構(只是用了映射),可是搜索的方法就是樹形,由於URL路徑就是個樹形.它按照樹的根節點
與子節點的關係進行判斷,譬如路徑"/home/select/usercourse",match在匹配的時候會首先匹配到"/"(假如咱們註冊了),其次是"/home",
以後逐層匹配下來,假如咱們沒註冊過"/home/select/usercourse",可是註冊了"/home/select/",那麼match就會匹配到這一層.而後返回
"/home/select/"的Handler以及url(pattern).match函數的匹配規則實如今pathMatch函數
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/'來作一樣的事情了.url
下面咱們來看muxEntry,h比較明確,就是咱們註冊的處理過程.pattern與ServeMux中m的key相同,也就是說是咱們
註冊的路徑,而explicit的用途實際上是比較隱晦的。程序會根據explicit判別這個路徑的Handler是不是用戶註冊的。
若是是程序爲了Redirect(詳細點擊這裏)而設定的,那麼在它是能夠被覆蓋的,不然就是不能夠被覆蓋的.spa
DefaultServeMux變量就是直接調用的這個函數。那麼這個函數只make了一個變量m,也就是爲map分配了空間。
在golang的語法中,結構體裏未聲明的變量也是存在的(即分配了內存),只不過是該類型的默認值,好比hosts就
被設置成了false.這個問題不過多解釋,有興趣的話能夠看一下golang中的相關內容code
這個函數是ServeMux用來匹配路徑的主要函數,因此看一下策略仍是很重要的.
函數中的參數pattern是咱們註冊的路徑,path是用戶請求的路徑router
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上接口
註釋寫的很清楚,這個函數就是處理URL,而後調用*ServeMux.handler().首先調用cleanPath清理請求URL中的不合法內容。若是存在不合法內容,
則將清理過的URL交由*ServeMux.handler()處理並得到匹配到的pattern,而後修改url.Path的內容並調用RedirectHandler.
若是內容合法,則直接調用*ServeMux.handler()並返回結果內存
調用ServeMux.match()(封裝了pathMatch函數)來得到匹配到的Handler以及對應pattern,若是ServeMux.hosts==true,那麼
傳入的參數爲host + path,若是找不到的話,調用NotFoundHandler函數,並將其結果返回.
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,那麼便是能夠覆蓋的.
ServeHTTP會檢測非法的URI(* ) 若是經過檢測就會調用自身的Handler()來返回註冊的Handler,隨後調用Handler的ServeHTTP方法