一個簡單 Go Web MVC 框架實現思路
阿新 • • 發佈:2019-02-04
需要的知識點
為了防止你的心裡不適,需要以下知識點:
- Go 基本知識
- Go 反射的深入理解
- 使用過框架
Go Web 伺服器搭建
package main import ( "fmt" "net/http" ) func do(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") //這個寫入到w的是輸出到客戶端的 } func main() { http.HandleFunc("/", do) //設定訪問的路由 http.ListenAndServe(":9090", nil) //設定監聽的埠 }
上面的例子呼叫了http預設的DefaultServeMux來新增路由,需要提供兩個引數,第一個引數是希望使用者訪問此資源的URL路徑(儲存在r.URL.Path),第二引數是即將要執行的函式,以提供使用者訪問的資源。
Go預設的路由新增是通過函式http.Handle
和http.HandleFunc
等來新增,底層都是呼叫了DefaultServeMux.Handle(pattern string, handler Handler)
,這個函式會把路由資訊儲存在一個map資訊中map[string]muxEntry。
Go監聽埠,然後接收到tcp連線會扔給Handler來處理,上面的例子預設nil即為http.DefaultServeMux
DefaultServeMux.ServeHTTP
函式來進行排程,遍歷之前儲存的map路由資訊,和使用者訪問的URL進行匹配,以查詢對應註冊的處理函式。
你可以通過文件檢視 http.ListenAndServe 的方法,第二個引數是 Handler 型別的介面,只要實現 Handler 介面,就可以實現自定義路由。
func ListenAndServe(addr string, handler Handler) error
實現自定義路由:
package main import ( "fmt" "net/http" ) type MyMux struct { } func (p*MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello myroute!") } func main() { mux := &MyMux{} http.ListenAndServe(":9090", mux) }
通過自定義路由,實現簡單 MVC 框架
兩個基本結構的定義:
type controllerInfo struct { url string controllerType reflect.Type } type ControllerRegistor struct { routers []*controllerInfo }
整體思路:
- controllerInfo url 是新增時候對應的路由, controllerType 反射型別。
- 通過 mux.Add("/", &DefaultController{}) 前臺新增的資訊,放到一個 routers []*controllerInfo 陣列中
- 每一次請求都會遍歷 routes, 判斷當前的 r.URL.Path 是否與 routes 裡面一個相等,如果相等, 通過型別反射,執行相應的方法。
流程圖:
原始碼實現
package main import ( "fmt" "net/http" "reflect" ) type MyMux struct { } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello myroute!") } type controllerInfo struct { url string controllerType reflect.Type } type ControllerRegistor struct { routers []*controllerInfo } type ControllerInterface interface { Do() } type UserController struct { } type DefaultController struct { } func (u *UserController) Do() { fmt.Println("I`m UserController") } func (d *DefaultController) Do() { fmt.Println("I`m DefaultController") } func (p *ControllerRegistor) Add(pattern string, c ControllerInterface) { //now create the Route t := reflect.TypeOf(c).Elem() route := &controllerInfo{} route.url = pattern route.controllerType = t p.routers = append(p.routers, route) } // AutoRoute func (p *ControllerRegistor) ServeHTTP(w http.ResponseWriter, r *http.Request) { var started bool requestPath := r.URL.Path fmt.Println(requestPath) //find a matching Route for _, route := range p.routers { if requestPath == route.url { vc := reflect.New(route.controllerType) method := vc.MethodByName("Do") method.Call(nil) started = true fmt.Fprintf(w, "Hello " + route.controllerType.Name()) break } } //if no matches to url, throw a not found exception if started == false { http.NotFound(w, r) } } func main() { mux := &ControllerRegistor{} mux.Add("/", &DefaultController{}) mux.Add("/user", &UserController{}) s := &http.Server{ Addr: ":9527", Handler: mux, } s.ListenAndServe() }
以上只是一個簡陋的實現思路,可以優化加上具體的功能和通過引數呼叫方法等。
參考連結: