gin原始碼學習-路由註冊(2)
阿新 • • 發佈:2022-12-08
gin框架主要是在標準庫net/http的基礎上對路由進行改寫,本文將從net/http
與gin
的路由註冊分享路由方面的一些理解。
1.net/http的路由註冊
1.1 路由註冊
首先來個demo:
package main import ( "log" "net/http" ) func main() { http.HandleFunc("/ping", pong) err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatalln("start http server fail: ", err) } } func pong(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pong")) }
可以看到,一個簡單的http server通過呼叫http.HandleFunc(path, handler)
實現路由的註冊,我們順著繼續看:
// HandleFunc registers the handler function for the given pattern // in the DefaultServeMux. // The documentation for ServeMux explains how patterns are matched. func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
可以看到,其實就是呼叫預設的servemux的HandleFunc()
方法,我們看看這個預設的servemux:
// DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = &defaultServeMux // 建立一個空的servemux var defaultServeMux ServeMux // ServeMux also takes care of sanitizing the URL request path and the Host // header, stripping the port number and redirecting any request containing . or // .. elements or repeated slashes to an equivalent, cleaner URL. type ServeMux struct { mu sync.RWMutex // 保證m的併發安全 m map[string]muxEntry // path與handler的對映 es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames }
接下來繼續看呼叫情況:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler)) // 呼叫內部的Handle方法註冊路由
}
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
// 保護mx.m
mux.mu.Lock()
defer mux.mu.Unlock()
// 處理空path
if pattern == "" {
panic("http: invalid pattern")
}
// 處理空handler
if handler == nil {
panic("http: nil handler")
}
// 處理已經存在的path
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
// 懶漢模式,為空就建立個map用來儲存路由資訊,path-handler
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
// 路由實體
e := muxEntry{h: handler, pattern: pattern}
// 註冊路由資訊
mux.m[pattern] = e
// 處理path最後帶“/”的
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
if pattern[0] != '/' {
mux.hosts = true
}
}
從原始碼來看,net/http註冊原始碼十分簡單粗暴,根本沒有restful的風格,不區分請求方法,GET/POST/DELETE一概不管,上生產日積月累,這程式碼好維護?
net/http的路由註冊就是簡單的通過map來儲存路由資訊,key為路由url,value為同時儲存url和handler的結構體,註冊前不存在就插入,存在就報錯,真的很簡單的處理。
1.2 net/http的請求處理
從gin的原始碼分析可以知道,最終的請求是通過具體的ServeHTTP方法實現,不妨看看servemux的ServeHTTP是怎樣處理的。
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r) // 獲取handler
h.ServeHTTP(w, r) // handler處理請求
}
// Handler returns the handler to use for the given request,
// consulting r.Method, r.Host, and r.URL.Path. It always returns
// a non-nil handler. If the path is not in its canonical form, the
// handler will be an internally-generated handler that redirects
// to the canonical path. If the host contains a port, it is ignored
// when matching handlers.
//
// The path and host are used unchanged for CONNECT requests.
//
// Handler also returns the registered pattern that matches the
// request or, in the case of internally-generated redirects,
// the pattern that will match after following the redirect.
//
// If there is no registered handler that applies to the request,
// Handler returns a “page not found” handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// CONNECT requests are not canonicalized.
if r.Method == "CONNECT" {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
return mux.handler(r.Host, r.URL.Path)
}
// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
return RedirectHandler(u.String(), StatusMovedPermanently), pattern
}
return mux.handler(host, r.URL.Path) // 返回handler
}
// handler的主要實現
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path) // 繼續看呼叫
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
// 真正地拿到handler
// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// Check for exact match first.
v, ok := mux.m[path] // map中匹配到直接返回
if ok {
return v.h, v.pattern
}
// Check for longest valid match. mux.es contains all patterns
// that end in / sorted from longest to shortest.
for _, e := range mux.es { // 處理含“/”的pattern
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
原始碼看完,順帶給個net/http實現的ServeHTTP的處理流程: