1. 程式人生 > 其它 >gin原始碼學習-路由註冊(2)

gin原始碼學習-路由註冊(2)

gin框架主要是在標準庫net/http的基礎上對路由進行改寫,本文將從net/httpgin的路由註冊分享路由方面的一些理解。

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的處理流程: