1. 程式人生 > >golang自定義路由控制實現(一)

golang自定義路由控制實現(一)

    由於本人之前一直是Java Coder,在Java web開發中其實大家都很依賴框架,所以當在學習Golang的時候,自己便想著在Go開發中脫離框架,自己動手造框架來練習。通過學習借鑑Java的思想還有部分框架的原始碼,在golang上面進行實現,從而達到對Java和Golang的同時學習目的,這就很美滋滋了。
    Golang中http的設計非常輕量,又兼具很高的擴充套件性,初學者都可以輕易的設計出自定義的路由功能,使用上十分簡單(這裡……來吐槽一下Java的Servlet,雖然我也對Java愛得深沉),下面請看Go的Demo。

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自帶的路由器有幾個限制:

  • 不支援引數設定,例如/user/:uid 這種泛型別匹配。
  • 無法很好的支援REST模式,無法限制訪問的方法,例如上面的例子中,使用者訪問/foo,可以用GET、POST、DELETE、HEAD等方式訪問。
  • 一般網站的路由規則太多了,編寫繁瑣,可以通過struct的方法進行一種簡化。
        Go有如此限制跟http提供的預設方式有關,我們先看下http兩個關鍵的struct
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來實現。

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的認識進一步提升。如果讀者有更好的方法,不吝賜教

http://news.dep9137.cn/
http://www.bed0568.cn/
http://www.dhg3119.cn/
http://www.opg6486.cn/
http://news.wdr5566.cn/
http://www.yyf8629.cn/
http://www.aua2439.cn/
http://www.kjx4882.cn/
http://news.idd5091.cn/
http://www.oka2713.top/
http://www.skk3561.cn/
http://www.nqa8573.cn/
http://www.jxb0956.cn/
http://www.nwf3326.cn/
http://www.mjg4415.cn/
http://www.kdx4817.top/
http://www.ozn1702.cn/
http://www.dyy3200.cn/
http://www.per1537.cn/
http://www.cqu4082.cn/
http://www.bin6562.cn/
http://www.iip1291.cn/
http://news.veo6686.cn/
http://news.tdw5546.cn/
http://www.yat8046.cn/
http://www.dyg4913.cn/
http://news.ipc6507.cn/
http://news.qfg6726.cn/
http://news.nxf9936.cn/
http://news.cun5054.cn/
http://news.ase4727.cn/
http://www.xip7382.cn/
http://www.wan2959.cn/
http://www.edr0603.top/
http://www.qnu9925.cn/
http://news.adw2245.cn/
http://www.bxb7451.cn/
http://www.gjc9646.cn/
http://www.acj2609.cn/
http://www.dwd9016.cn/
http://www.prl0026.cn/
http://news.gfw0394.cn/
http://news.xah7645.cn/
http://www.ngp3761.cn/
http://www.soq5741.top/
http://www.axz7045.cn/
http://www.jta0960.cn/
http://www.jbx0190.cn/
http://www.wll1115.cn/
http://news.lsz6488.cn/
http://www.yio4898.cn/
http://news.sml6389.cn/
http://www.rrq5611.top/
http://www.dja2819.cn/
http://news.bnb6875.cn/
http://news.dmi4893.cn/
http://news.mar4014.cn/
http://news.dwd9016.cn/