首页 > 编程语言 >gin源码学习-路由注册(2)

gin源码学习-路由注册(2)

时间:2022-12-08 16:57:54浏览次数:54  
标签:http URL pattern mux handler path gin 源码 路由

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

标签:http,URL,pattern,mux,handler,path,gin,源码,路由
From: https://www.cnblogs.com/davis12/p/16966542.html

相关文章