因爲本人以前一直是Java Coder,在Java web開發中其實你們都很依賴框架,因此當在學習Golang的時候,本身便想着在Go開發中脫離框架,本身動手造框架來練習。經過學習借鑑Java的思想還有部分框架的源碼,在golang上面進行實現,從而達到對Java和Golang的同時學習目的,這就很美滋滋了。
Golang中http的設計很是輕量,又兼具很高的擴展性,初學者均可以輕易的設計出自定義的路由功能,使用上十分簡單(這裏……來吐槽一下Java的Servlet,雖然我也對Java愛得深沉),下面請看Go的Demo。golang
func HelloServer1(w http.ResponseWriter, req *http.Request) { fmt.Fprint(w,"hello world") } func main() { http.HandleFunc("/test", HelloServer1) err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("ListenAndServe: ", err.Error()) } }
短短的幾行代碼即可以成功註冊一個接口並跑起服務。可是原生的開發方式提供的功能是比較精簡的目前幾乎全部的Web應用路由實現都是基於http默認的路由器,可是Go自帶的路由器有幾個限制:web
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 }
咱們須要重點關鍵兩個地方,一個是ServeMux 中的參數m,它的類型是 map[string]muxEntry ,這裏咱們天然而然能夠想到,參數m負責路由分發。第二個重點則是muxEntry,muxEntry的h Handler
對應的就是咱們編寫的接口,而圍繞這個接口,http並無其餘過多的功能,甚至連像Java中制定一套統一web開發標準都沒有。所以http中只是提供最基礎的功能,用戶則須要以這些功能爲基礎,進而YY出本身想要的框架或者更豐富的功能。
首先咱們問題,可以快速簡單的設置Http Method,以方便往後支持RESTFUL的URL規範。有兩種簡單的作法,第一種作法是使用二維Map ,即map[string]map[string]http.HandlerFunc
,其中一維的鍵String表示請求method好比post, get 等。二維的鍵string表示要匹配的URL地址, http.HandlerFunc固然就是處理URL請求的具體方法。第二種作法便是筆者採用的作法,實際上是第一種作法演變而來的,HTTP 中Method的種類是固定的,其實咱們徹底能夠用一個數組,而值爲map[string]http.HandlerFunc
來實現。segmentfault
const ( GET = iota POST PUT DELETE CONNECTIBNG HEAD OPTIONS PATCH TRACE )
看完上面常量的設置,想必讀者已經知道了個人意思,e.g:array[0]表示GET方法下全部的接口的集合,array[1]表示POST方法下全部的接口的集合基本原理其實也簡單,把Get方法下的全部的接口都存儲到array[0]的值中,以此推理其餘方法。原理簡單,可是一個框架的設計必須高內聚低耦合,一個Web框架中路由分發是基礎,在該此處上須要創建更多的功能,好比說過濾器等。在初期設計的時候必須保證要有可擴展性,因此筆者認爲難點在於此。下面直接上代碼,對應的代碼有充分的註釋。數組
package odserver import ( "net/http" ) //實現IOdServer的接口,以及http提供ServeHttp方法 type OdServer struct { router MethodMaps } type IOdServer interface { GET(url string, f HandlerFunc) POST(url string, f HandlerFunc) PUT(url string, f HandlerFunc) DELETE(url string, f HandlerFunc) } type HandlerMapped struct { f HandlerFunc } //接口函數單位,即咱們編寫代碼邏輯的函數 type HandlerFunc func(w http.ResponseWriter, req *http.Request) func Default() *OdServer { return &OdServer{ router:NewRouter(), } } //實現Handler接口,匹配方法以及路徑 func (o *OdServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { //轉發給doHandler進行執行 o.doHandler(w,req) } //判斷須要執行的Http Method,從而查找對應的接口而且執行 func (o *OdServer) doHandler(w http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodGet: { if hm, ok := o.router.GetMapping(req.URL.RequestURI()); ok { hm.f(w, req) } } case http.MethodPost: { if hm, ok := o.router.PostMapping(req.URL.RequestURI()); ok { hm.f(w, req) } } case http.MethodDelete: { if hm, ok := o.router.DeleteMapping(req.URL.String()); ok { hm.f(w, req) } } case http.MethodPut: { if hm, ok := o.router.PutMapping(req.URL.String()); ok { hm.f(w, req) } } default: { } } } func (o *OdServer) GET(url string, f HandlerFunc) { o.router.GetAdd(url, HandlerMapped{f: f}) } func (o *OdServer) POST(url string, f HandlerFunc) { o.router.PostAdd(url, HandlerMapped{f: f}) } func (o *OdServer) PUT(url string, f HandlerFunc) { o.router.PutAdd(url, HandlerMapped{f: f}) } func (o *OdServer) DELETE(url string, f HandlerFunc) { o.router.DeleteAdd(url, HandlerMapped{f: f}) }
package odserver /** 提供基本的路由功能,添加路由,查找路由 */ const ( GET = iota POST PUT DELETE CONNECTIBNG HEAD OPTIONS PATCH TRACE ) func NewRouter() MethodMaps { return []handler{ GET: make(handler), POST: make(handler), PUT: make(handler), DELETE: make(handler), } } type MethodMaps [] handler type handler map[string]HandlerMapped //映射路由,獲取Get方法下對應的接口 func (m MethodMaps) GetMapping(url string) (HandlerMapped, bool) { if hm, ok := m[GET][url]; ok { return hm, true } return HandlerMapped{}, false } //映射路由,獲取Post方法下對應的接口 func (m MethodMaps) PostMapping(url string) (HandlerMapped, bool) { if hm, ok := m[POST][url]; ok { return hm, true } return HandlerMapped{}, false } //映射路由,獲取Delete方法下對應的接口 func (m MethodMaps) DeleteMapping(url string) (HandlerMapped, bool) { if hm, ok := m[DELETE][url]; ok { return hm, true } return HandlerMapped{}, false } //映射路由,獲取Put方法下對應的接口 func (m MethodMaps) PutMapping(url string) (HandlerMapped, bool) { if hm, ok := m[PUT][url]; ok { return hm, true } return HandlerMapped{}, false } //增長Get方法下的接口 func (m MethodMaps) GetAdd(url string, mapped HandlerMapped) { if _, ok := m.GetMapping(url); ok { panic("duplicate url with get method") } m[GET].SetUrl(url,mapped) } //增長Post方法下的接口 func (m MethodMaps) PostAdd(url string, mapped HandlerMapped) { if _, ok := m.GetMapping(url); ok { panic("duplicate url with Post method") } m[POST].SetUrl(url,mapped) } //增長Put方法下的接口 func (m MethodMaps) PutAdd(url string, mapped HandlerMapped) { if _, ok := m.GetMapping(url); ok { panic("duplicate url with Put method") } m[PUT].SetUrl(url,mapped) } //增長Delete方法下的接口 func (m MethodMaps) DeleteAdd(url string, mapped HandlerMapped) { if _, ok := m.GetMapping(url); ok { panic("duplicate url with Delete method") } m[DELETE].SetUrl(url,mapped) } func (h handler) SetUrl(url string, mapped HandlerMapped) { h[url] = mapped }
如我所說,我以爲學習Golang比較有意思的是,能夠將從Java裏學到的東西,轉之在Golang裏嘗試實現,不只學習了Golang,還使得本身對Java的認識進一步提高。若是讀者有更好的方法,不吝賜教
參考資料:# Golang學習筆記 - 標準庫"net/http"的簡析及自制簡單路由框架app