Golang學習筆記 - 標準庫"net/http"的簡析及自制簡單路由框架

原文連接:http://targetliu.com/golang-http-router/golang

仍是在繼續學習Go的路上,曾經在使用PHP的時候吃過過分依賴框架的虧。如今學習Go的時候決定先打好基礎,從標準庫學起走。服務器

源碼分析

咱們知道最簡單的創建http服務器代碼基本上都是這樣的:框架

http.HandleFunc('/', func(w http.ResponseWriter, r *http.Request){
    fmt.Fprint(w, "Hello world")
})
http.ListenAndServe(":8080", nil)

這樣就成功的創建了一個監聽 8080 端口的http服務器,當訪問的時候輸出 Hello world 函數

咱們順藤摸瓜來看看 HandleFunc 作了些什麼事:源碼分析

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

這裏繼續經過調用 DefaultServeMuxHandleFunc 方法註冊路由,這個 DefaultServeMux 又是何方聖神:學習

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
}

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

DefaultServeMuxnet/http 包提供的一個默認的 ServeMux 類型,ServeMux 實現了 Handler 接口。code

追根究底,發現http服務器收到一條請求後經過 go c.serve(ctx) 開啓goroutine 處理這個請求,在這個過程當中調用了 Handler 接口函數 ServeHTTP 來作進一步的處理(好比匹配方法、連接等等)。router

因此,咱們就能夠理解 ServeMux 就是 net/http 一個內置的路由功能。接口

繼續回到 HandleFunc 來:ci

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

ServeMuxHandleFunc 方法將咱們傳入的路由具體實現函數轉換成 HandlerFunc 類型並經過 Handle 註冊到路由。這個 HandlerFunc 類型也實現了 Handler 接口:

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

最後到了 Handle 這個方法, Handle 方法經過將 pattern 路徑以及實現了 Handler 接口的方法一一對應的保存到 ServeMuxmap[string]muxEntry 中,方便後續請求的時候調用。所以,也能夠經過 Handle 直接傳入一個實現了 Handler 接口的方法註冊路由。

至此,net/http 包中默認路由的註冊過程基本上已經走完。

至於請求的時候路由調用,記住經過 ServeHTTP 查找 map 中對應路徑並調用相關方法就好了。

自制路由

經過以上的分析,咱們能夠依樣畫葫蘆,實現本身的路由功能。

package route

import (
    "net/http"
    "strings"
)

//返回一個Router實例
func NewRouter() *Router {
    return new(Router)
}

//路由結構體,包含一個記錄方法、路徑的map
type Router struct {
    Route map[string]map[string]http.HandlerFunc
}

//實現Handler接口,匹配方法以及路徑
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    if h, ok := r.Route[req.Method][req.URL.String()]; ok {
        h(w, req)
    }
}

//根據方法、路徑將方法註冊到路由
func (r *Router) HandleFunc(method, path string, f http.HandlerFunc) {
    method = strings.ToUpper(method)
    if r.Route == nil {
        r.Route = make(map[string]map[string]http.HandlerFunc)
    }
    if r.Route[method] == nil {
        r.Route[method] = make(map[string]http.HandlerFunc)
    }
    r.Route[method][path] = f
}

使用:

r := route.NewRouter()
r.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello Get!")
})
r.HandleFunc("POST", "/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "hello POST!")
})
http.ListenAndServe(":8080", r)

這個例子只是依樣畫葫蘆的簡單功能實現。

一個完整的路由框架應該包含更復雜的匹配、錯誤檢測等等功能,你們能夠試着本身動手試試。

閱讀源碼和重複造輪子都是學習的方法。

最後,歡迎你們關注個人博客http://targetliu.com/

相關文章
相關標籤/搜索