Gin源码解析

结构

<<interface>> IRoutes Engine RouterGroup HandlersChain Context

Engine的结构为

go 复制代码
type Engine struct {
	RouterGroup
	routeTreesUpdated sync.Once
	RedirectTrailingSlash bool
	RedirectFixedPath bool
	HandleMethodNotAllowed bool
	ForwardedByClientIP bool
	AppEngine bool
	UseRawPath bool
	UseEscapedPath bool
	UnescapePathValues bool
	RemoveExtraSlash bool
	RemoteIPHeaders []string
	TrustedPlatform string
	MaxMultipartMemory int64
	UseH2C bool
	ContextWithFallback bool
	delims           render.Delims
	secureJSONPrefix string
	HTMLRender       render.HTMLRender
	FuncMap          template.FuncMap
	allNoRoute       HandlersChain
	allNoMethod      HandlersChain
	noRoute          HandlersChain
	noMethod         HandlersChain
	pool             sync.Pool
	trees            methodTrees
	maxParams        uint16
	maxSections      uint16
	trustedProxies   []string
	trustedCIDRs     []*net.IPNet
}

allNoRoute:没有找到路由时的处理链
allNoMethod:方法没有找到时的处理链
RouterGroup结构为

go 复制代码
type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

HandlersChain为函数切片

go 复制代码
type HandlerFunc func(*Context)
type HandlersChain []HandlerFunc

Context为处理中的关键数据结构

go 复制代码
type Context struct {
	writermem responseWriter
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
	handlers HandlersChain
	index    int8
	fullPath string

	engine       *Engine
	params       *Params
	skippedNodes *[]skippedNode

	// This mutex protects Keys map.
	mu sync.RWMutex

	// Keys is a key/value pair exclusively for the context of each request.
	Keys map[any]any

	// Errors is a list of errors attached to all the handlers/middlewares who used this context.
	Errors errorMsgs

	// Accepted defines a list of manually accepted formats for content negotiation.
	Accepted []string

	// queryCache caches the query result from c.Request.URL.Query().
	queryCache url.Values

	// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
	// or PUT body parameters.
	formCache url.Values

	// SameSite allows a server to define a cookie attribute making it impossible for
	// the browser to send this cookie along with cross-site requests.
	sameSite http.SameSite
}

创建

创建Engine是通过Default方法

  • 先使用New()创建Engine
  • 使用OptionFunc函数来配置Engine
go 复制代码
func Default(opts ...OptionFunc) *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine.With(opts...)
}

func New(opts ...OptionFunc) *Engine {
	debugPrintWARNINGNew()
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			basePath: "/",
			root:     true,
		},
		FuncMap:                template.FuncMap{},
		RedirectTrailingSlash:  true,
		RedirectFixedPath:      false,
		HandleMethodNotAllowed: false,
		ForwardedByClientIP:    true,
		RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},
		TrustedPlatform:        defaultPlatform,
		UseRawPath:             false,
		UseEscapedPath:         false,
		RemoveExtraSlash:       false,
		UnescapePathValues:     true,
		MaxMultipartMemory:     defaultMultipartMemory,
		trees:                  make(methodTrees, 0, 9),
		delims:                 render.Delims{Left: "{{", Right: "}}"},
		secureJSONPrefix:       "while(1);",
		trustedProxies:         []string{"0.0.0.0/0", "::/0"},
		trustedCIDRs:           defaultTrustedCIDRs,
	}
	engine.engine = engine
	engine.pool.New = func() any {
		return engine.allocateContext(engine.maxParams)
	}
	return engine.With(opts...)
}

func (engine *Engine) With(opts ...OptionFunc) *Engine {
	for _, opt := range opts {
		opt(engine)
	}

	return engine
}

配置Engine

通过OptionFunc来配置engine
OptionFunc 为函数别名

go 复制代码
type OptionFunc func(*Engine)

运行

是通过Run来执行

  • 在没有指定地址信息时,读取环境变量PORT端口号,没有则默认使用端口号8080
  • 内部创建http.Server,handler为engine.Handler(),执行http.Server的方法ListenAndServe,其中Engine是实现了http.Handler接口
go 复制代码
func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()

	if engine.isUnsafeTrustedProxies() {
		debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
			"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
	}
	engine.updateRouteTrees()
	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	server := &http.Server{ // #nosec G112
		Addr:    address,
		Handler: engine.Handler(),
	}
	err = server.ListenAndServe()
	return
}

Engine实现了http.Handler接口方法ServeHTTP,内部调用handleHTTPRequest,根据请求方法和请求url,找到对应的HandlersChain,执行Context的Next()方法,遍历处理链执行处理

go 复制代码
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	engine.routeTreesUpdated.Do(func() {
		engine.updateRouteTrees()
	})

	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(c *Context) {
	httpMethod := c.Request.Method
	rPath := c.Request.URL.Path
	unescape := false

	if engine.UseEscapedPath {
		rPath = c.Request.URL.EscapedPath()
		unescape = engine.UnescapePathValues
	} else if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
		rPath = c.Request.URL.RawPath
		unescape = engine.UnescapePathValues
	}

	if engine.RemoveExtraSlash {
		rPath = cleanPath(rPath)
	}

	// Find root of the tree for the given HTTP method
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method != httpMethod {
			continue
		}
		root := t[i].root
		// Find route in tree
		value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
		if value.params != nil {
			c.Params = *value.params
		}
		if value.handlers != nil {
			c.handlers = value.handlers
			c.fullPath = value.fullPath
			c.Next()
			c.writermem.WriteHeaderNow()
			return
		}
		if httpMethod != http.MethodConnect && rPath != "/" {
			if value.tsr && engine.RedirectTrailingSlash {
				redirectTrailingSlash(c)
				return
			}
			if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
				return
			}
		}
		break
	}

	if engine.HandleMethodNotAllowed && len(t) > 0 {
		// According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
		// containing a list of the target resource's currently supported methods.
		allowed := make([]string, 0, len(t)-1)
		for _, tree := range engine.trees {
			if tree.method == httpMethod {
				continue
			}
			if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
				allowed = append(allowed, tree.method)
			}
		}
		if len(allowed) > 0 {
			c.handlers = engine.allNoMethod
			c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
			serveError(c, http.StatusMethodNotAllowed, default405Body)
			return
		}
	}

	c.handlers = engine.allNoRoute
	serveError(c, http.StatusNotFound, default404Body)
}

func (c *Context) Next() {
	c.index++
	for c.index < safeInt8(len(c.handlers)) {
		if c.handlers[c.index] != nil {
			c.handlers[c.index](c)
		}
		c.index++
	}
}

插件管理

插件即HandlerFunc,其定义为

go 复制代码
type HandlerFunc func(*Context)

添加插件使用Use,将HandlerFunc 添加到engine.RouterGroup中,最终是添加到RouterGroup中的Handlers

其定义为

go 复制代码
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
	group.Handlers = append(group.Handlers, middleware...)
	return group.returnObj()
}

func (group *RouterGroup) returnObj() IRoutes {
	if group.root {
		return group.engine
	}
	return group
}

在设置路由handler时,会通过RouterGroup的combineHandlers将中间件的handler合并添加到路由handler中

go 复制代码
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
	finalSize := len(group.Handlers) + len(handlers)
	assert1(finalSize < int(abortIndex), "too many handlers")
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers)
	copy(mergedHandlers[len(group.Handlers):], handlers)
	return mergedHandlers
}
相关推荐
田梓燊13 小时前
2026/4/11 leetcode 3741
数据结构·算法·leetcode
斯内科14 小时前
FFT快速傅里叶变换
算法·fft
2301_8227032014 小时前
开源鸿蒙跨平台Flutter开发:幼儿疫苗全生命周期追踪系统:基于 Flutter 的免疫接种档案与状态机设计
算法·flutter·华为·开源·harmonyos·鸿蒙
贵慜_Derek14 小时前
Managed Agents 里,Harness 到底升级了什么?
人工智能·算法·架构
2301_8227032014 小时前
鸿蒙flutter三方库实战——教育与学习平台:Flutter Markdown
学习·算法·flutter·华为·harmonyos·鸿蒙
Jia ming14 小时前
C语言实现日期天数计算
c语言·开发语言·算法
无限进步_15 小时前
【C++&string】大数相乘算法详解:从字符串加法到乘法实现
java·开发语言·c++·git·算法·github·visual studio
苏纪云15 小时前
蓝桥杯考前突击
c++·算法·蓝桥杯
W230357657315 小时前
经典算法详解:最长公共子序列 (LCS) —— 从暴力递归到动态规划完整实现
算法·动态规划·最长子序列
pzx_00115 小时前
【优化器】 随机梯度下降 SGD 详解
人工智能·python·算法