這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原文連結:http://targetliu.com/golang-http-router/
還是在繼續學習Go的路上,曾經在使用PHP的時候吃過過度依賴架構的虧。現在學習Go的時候決定先打好基礎,從標準庫學起走。
源碼分析
我們知道最簡單的建立http伺服器代碼基本上都是這樣的:
http.HandleFunc('/', func(w http.ResponseWriter, r *http.Request){ fmt.Fprint(w, "Hello world")})http.ListenAndServe(":8080", nil)
這樣就成功的建立了一個監聽 8080 連接埠的http伺服器,當訪問的時候輸出 Hello world
我們順藤摸瓜來看看 HandleFunc 做了些什麼事:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
這裡繼續通過調用 DefaultServeMux 的 HandleFunc 方法註冊路由,這個 DefaultServeMux 又是何方聖神:
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}// NewServeMux allocates and returns a new ServeMux.func NewServeMux() *ServeMux { return new(ServeMux) }// DefaultServeMux is the default ServeMux used by Serve.var DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux
DefaultServeMux 是 net/http 包提供的一個預設的 ServeMux 類型,ServeMux 實現了 Handler 介面。
追根究底,發現http伺服器收到一條請求後通過 go c.serve(ctx) 開啟goroutine 處理這個請求,在這個過程中調用了 Handler 介面函數 ServeHTTP 來做進一步的處理(比如匹配方法、連結等等)。
所以,我們就可以理解 ServeMux 就是 net/http 一個內建的路由功能。
繼續回到 HandleFunc 來:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler))}
ServeMux 的 HandleFunc 方法將我們傳入的路由具體實現函數轉換成 HandlerFunc 類型並通過 Handle 註冊到路由。這個 HandlerFunc 類型也實現了 Handler 介面:
type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
最後到了 Handle 這個方法, Handle 方法通過將 pattern 路徑以及實現了 Handler 介面的方法一一對應的儲存到 ServeMux 的 map[string]muxEntry 中,方便後續請求的時候調用。因此,也可以通過 Handle 直接傳入一個實現了 Handler 介面的方法註冊路由。
至此,net/http 包中預設路由的註冊過程基本上已經走完。
至於請求的時候路由調用,記住通過 ServeHTTP 尋找 map 中對應路徑並調用相關方法就行了。
自製路由
通過以上的分析,我們可以依樣畫葫蘆,實現自己的路由功能。
package routeimport ( "net/http" "strings")//返回一個Router執行個體func NewRouter() *Router { return new(Router)}//路由結構體,包含一個記錄方法、路徑的maptype Router struct { Route map[string]map[string]http.HandlerFunc}//實現Handler介面,匹配方法以及路徑func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { if h, ok := r.Route[req.Method][req.URL.String()]; ok { h(w, req) }}//根據方法、路徑將方法註冊到路由func (r *Router) HandleFunc(method, path string, f http.HandlerFunc) { method = strings.ToUpper(method) if r.Route == nil { r.Route = make(map[string]map[string]http.HandlerFunc) } if r.Route[method] == nil { r.Route[method] = make(map[string]http.HandlerFunc) } r.Route[method][path] = f}
使用:
r := route.NewRouter()r.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello Get!")})r.HandleFunc("POST", "/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "hello POST!")})http.ListenAndServe(":8080", r)
這個例子只是依樣畫葫蘆的簡易功能實現。
一個完整的路由架構應該包含更複雜的匹配、錯誤偵測等等功能,大家可以試著自己動手試試。
閱讀源碼和重複造輪子都是學習的方法。
最後,歡迎大家關注我的部落格http://targetliu.com/