原文連接:http://targetliu.com/golang-http-router/golang
仍是在繼續學習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 = &defaultServeMux var defaultServeMux ServeMux
DefaultServeMux
是 net/http
包提供的一個默認的 ServeMux
類型,ServeMux
實現了 Handler
接口。code
追根究底,發現http服務器收到一條請求後經過 go c.serve(ctx)
開啓goroutine
處理這個請求,在這個過程當中調用了 Handler
接口函數 ServeHTTP
來作進一步的處理(好比匹配方法、連接等等)。router
因此,咱們就能夠理解 ServeMux
就是 net/http
一個內置的路由功能。接口
繼續回到 HandleFunc
來:ci
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 route import ( "net/http" "strings" ) //返回一個Router實例 func NewRouter() *Router { return new(Router) } //路由結構體,包含一個記錄方法、路徑的map type 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/