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