1. 程式人生 > >一個簡單 Go Web MVC 框架實現思路

一個簡單 Go Web MVC 框架實現思路

 需要的知識點

   為了防止你的心裡不適,需要以下知識點:

  • 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.Handlehttp.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()
}

 以上只是一個簡陋的實現思路,可以優化加上具體的功能和通過引數呼叫方法等。

 參考連結: