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

????由于本人之前一直是Java Coder,在Java web開發(fā)中其實大家都很依賴框架肺孤,所以當在學習Golang的時候始鱼,自己便想著在Go開發(fā)中脫離框架,自己動手造框架來練習鸯旁。通過學習借鑒Java的思想還有部分框架的源碼噪矛,在golang上面進行實現(xiàn),從而達到對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())
    }
}

????短短的幾行代碼便可以成功注冊一個接口并跑起服務苞冯。但是原生的開發(fā)方式提供的功能是比較精簡的目前幾乎所有的Web應用路由實現(xiàn)都是基于http默認的路由器,但是Go自帶的路由器有幾個限制:

  • 不支持參數(shù)設定侧巨,例如/user/:uid 這種泛類型匹配舅锄。
  • 無法很好的支持REST模式,無法限制訪問的方法司忱,例如上面的例子中巧娱,用戶訪問/foo,可以用GET烘贴、POST禁添、DELETE、HEAD等方式訪問桨踪。
  • 一般網(wǎng)站的路由規(guī)則太多了老翘,編寫繁瑣,可以通過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 中的參數(shù)m汽纠,它的類型是 map[string]muxEntry 卫键,這里我們自然而然可以想到,參數(shù)m負責路由分發(fā)虱朵。第二個重點則是muxEntry莉炉,muxEntry的h Handler 對應的就是我們編寫的接口,而圍繞這個接口碴犬,http并沒有其他過多的功能絮宁,甚至連像Java中制定一套統(tǒng)一web開發(fā)標準都沒有。因此http中只是提供最基礎的功能服协,用戶則需要以這些功能為基礎绍昂,進而YY出自己想要的框架或者更豐富的功能。
????首先我們問題偿荷,能夠快速簡單的設置Http Method窘游,以方便日后支持RESTFUL的URL規(guī)范。有兩種簡單的做法跳纳,第一種做法是使用二維Map 忍饰,即map[string]map[string]http.HandlerFunc,其中一維的鍵String表示請求method比如post, get 等。二維的鍵string表示要匹配的URL地址棒旗, http.HandlerFunc當然就是處理URL請求的具體方法喘批。第二種做法即是筆者采用的做法撩荣,其實是第一種做法演變而來的,HTTP 中Method的種類是固定的饶深,其實我們完全可以用一個數(shù)組餐曹,而值為map[string]http.HandlerFunc來實現(xiàn)。

const (
    GET         = iota
    POST
    PUT
    DELETE
    CONNECTIBNG
    HEAD
    OPTIONS
    PATCH
    TRACE
)

????看完上面常量的設置敌厘,想必讀者已經知道了我的意思台猴,e.g:array[0]表示GET方法下所有的接口的集合,array[1]表示POST方法下所有的接口的集合基本原理其實也簡單,把Get方法下的所有的接口都存儲到array[0]的值中俱两,以此推理其他方法饱狂。原理簡單,但是一個框架的設計必須高內聚低耦合宪彩,一個Web框架中路由分發(fā)是基礎休讳,在該此處上需要建立更多的功能,比如說過濾器等尿孔。在初期設計的時候必須保證要有可擴展性俊柔,所以筆者認為難點在于此。下面直接上代碼活合,對應的代碼有充分的注釋雏婶。

/**
odserver.go
*/
package odserver

import (
    "net/http"
)
//實現(xiàn)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
}
//接口函數(shù)單位白指,即我們編寫代碼邏輯的函數(shù)
type HandlerFunc func(w http.ResponseWriter, req *http.Request)

func Default() *OdServer {
    return &OdServer{
        router:NewRouter(),
    }
}

//實現(xiàn)Handler接口留晚,匹配方法以及路徑
func (o *OdServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    //轉發(fā)給doHandler進行執(zhí)行
    o.doHandler(w,req)
}
//判斷需要執(zhí)行的Http Method,從而查找對應的接口并且執(zhí)行
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})
}

/**
router.go
*/
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里嘗試實現(xiàn)缸血,不僅學習了Golang,還使得自己對Java的認識進一步提升械筛。如果讀者有更好的方法捎泻,不吝賜教
參考資料:# Golang學習筆記 - 標準庫"net/http"的簡析及自制簡單路由框架

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市埋哟,隨后出現(xiàn)的幾起案子笆豁,更是在濱河造成了極大的恐慌郎汪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闯狱,死亡現(xiàn)場離奇詭異煞赢,居然都是意外死亡,警方通過查閱死者的電腦和手機哄孤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門照筑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘦陈,你說我怎么就攤上這事凝危。” “怎么了晨逝?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵蛾默,是天一觀的道長。 經常有香客問我捉貌,道長支鸡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任昏翰,我火速辦了婚禮苍匆,結果婚禮上,老公的妹妹穿的比我還像新娘棚菊。我一直安慰自己浸踩,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布统求。 她就那樣靜靜地躺著检碗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪码邻。 梳的紋絲不亂的頭發(fā)上折剃,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音像屋,去河邊找鬼怕犁。 笑死,一個胖子當著我的面吹牛己莺,可吹牛的內容都是我干的奏甫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凌受,長吁一口氣:“原來是場噩夢啊……” “哼阵子!你這毒婦竟也來了?” 一聲冷哼從身側響起胜蛉,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挠进,失蹤者是張志新(化名)和其女友劉穎色乾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體领突,經...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡暖璧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了攘须。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漆撞。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖于宙,靈堂內的尸體忽然破棺而出浮驳,到底是詐尸還是另有隱情,我是刑警寧澤捞魁,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布至会,位于F島的核電站,受9級特大地震影響谱俭,放射性物質發(fā)生泄漏奉件。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一昆著、第九天 我趴在偏房一處隱蔽的房頂上張望县貌。 院中可真熱鬧,春花似錦凑懂、人聲如沸煤痕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摆碉。三九已至,卻和暖如春脓豪,著一層夾襖步出監(jiān)牢的瞬間巷帝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工扫夜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留楞泼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓笤闯,卻偏偏與公主長得像现拒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子望侈,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)勋桶,斷路器脱衙,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架侥猬,建立于...
    Hsinwong閱讀 22,405評論 1 92
  • 翻看黃永玉的圖畫書時荤胁,四歲半的兒子看見有一幅畫是一只鴿子仰著脖子瞧预,便問我:“媽媽,這是不是就是曲項向天歌(鴿)仅政?”...
    樊江艷閱讀 389評論 2 5
  • 還是不能看太有代入感的小說垢油,,這兩天腦袋里全是那本小說的內容圆丹,滩愁,,
    我有個帥哥夢閱讀 124評論 0 0
  • 我對食物的癮到底是什么妻味? 很多時候真的不是因為餓才要吃東西。而是單獨為了滿足自己的口欲欣福,無論吃多少東西责球,都沒有飽的...
    洛洛Ell閱讀 194評論 0 0