简介
Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
- 项目地址
[https://github.com/gin-gonic/gin](https://github.com/gin-gonic/gin)
```go package main
import “github.com/gin-gonic/gin”
func main() { r := gin.Default() r.GET(“/ping”, func(c *gin.Context) { c.JSON(200, gin.H{ “message”: “pong”, }) }) r.Run() // listen and serve on 0.0.0.0:8080 (for windows “localhost:8080”) }
<a name="MlbXu"></a>
## Engine
- Engine是给用户使用的具体实例
- 提供了muxer、middleware、configuration settings功能
- 注意Engine复用了RouterGroup,因此它也是一个根路由,具有路由组的相关功能
```go
type Engine struct {
//实现路由功能, Engine即是根路由
RouterGroup
delims render.Delims
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
//pool是gin实现的对象池,针对Context等频繁创建的对象提供缓存功能
pool sync.Pool
//由url和调用链构成的radix tree, 用于路由请求
trees methodTrees
maxParams uint16
maxSections uint16
trustedProxies []string
trustedCIDRs []*net.IPNet
}
- Engine有两个初始化方法
- New提供基本的Engine,不附带任何middleware
- Default基于基本的Engine,提供log和recover功能的middleware
```go
func New() *Engine {
engine := &Engine{
RouterGroup: RouterGroup{
}, //… } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { return engine.allocateContext() } return engine }Handlers: nil,
basePath: "/",
root: true,
func Default() *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine }
- 核心方法
- Run启动函数,开始监听并处理请求
```go
func (engine *Engine) Run(addr ...string) (err error) {
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
//可以看到网络层主要是复用go的http库,engine本身实现了ServeHttp()
err = http.ListenAndServe(address, engine)
return
}
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
//从对象池取一个Context
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
//对象重置
c.reset()
engine.handleHTTPRequest(c)
//归还对象
engine.pool.Put(c)
}
//handleHTTPRequest 根据请求执行对应的处理流程
func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method
//1. 根据requestMethod找到对应的radix tree
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
//2. 根据url在radix tree中找到对应的节点数据
value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
if value.params != nil {
c.Params = *value.params
}
if value.handlers != nil {
//3. 为Context设置handlers调用链
c.handlers = value.handlers
c.fullPath = value.fullPath
//4. 开始执行调用链
c.Next()
c.writermem.WriteHeaderNow()
return
}
break
}
//5. 针对找不到handler的请求进行处理
if engine.HandleMethodNotAllowed {
for _, tree := range engine.trees {
if tree.method == httpMethod {
continue
}
if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
c.handlers = engine.allNoMethod
serveError(c, http.StatusMethodNotAllowed, default405Body)
return
}
}
}
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
addRoute注册请求处理方法,通过RouterGroup进行注册的方法最终都会调用Engine.addRoute,将对应的url和调用链添加到Engine的radix tree中。所以Gin中请求处理方法都是维护在Engine上的,而不是在RouterGroup,RouterGroup更多的是提供给用户向使用的东西
func (n *node) addRoute(path string, handlers HandlersChain) {
fullPath := path
n.priority++
//1. 空树, 将节点添加到父节点的子节点中
if len(n.path) == 0 && len(n.children) == 0 {
n.insertChild(path, fullPath, handlers)
n.nType = root
return
}
//2. 查找最长公共前缀, 并插入节点
walk:
for {
i := longestCommonPrefix(path, n.path)
//2.1 当前节点和待插入节点存在部分公共前缀,需要分裂当前节点(公共部分、非公共部分)
if i < len(n.path) {
//2.1.1 分裂非公共部分,并继承当前节点的子节点们
child := node{
path: n.path[i:],
wildChild: n.wildChild,
indices: n.indices,
children: n.children,
handlers: n.handlers,
priority: n.priority - 1,
fullPath: n.fullPath,
}
//2.1.2 修改当前节点的path,并把分裂出来的节点作为当前节点的子节点
n.children = []*node{&child} n.indices = bytesconv.BytesToString([]byte{n.path[i]})
n.path = path[:i]
n.handlers = nil
n.wildChild = false
n.fullPath = fullPath[:parentFullPathIndex+i]
}
//2.2 待插入节点和当前节点存在部分公共前缀,需要确定待插入节点和当前节点的子节点
//是否存在公共前缀, 若存在则取对应子节点继续执行walk过程; 否则直接将待插入节点
//插入到当前节点的子节点中
if i < len(path) {
path = path[i:] //取待插入节点的非公共部分
c := path[0]
//2.2.1 检查是否和子节点存在公共前缀(首字母), 存在则取该子节点, 继续walk
for i, max := 0, len(n.indices); i < max; i++ {
if c == n.indices[i] {
parentFullPathIndex += len(n.path)
i = n.incrementChildPrio(i)
n = n.children[i]
continue walk
}
}
//2.2.2 和子节点不存在公共部分, 直接插入到子节点中
if c != ':' && c != '*' && n.nType != catchAll {
//通配符检查
} else if n.wildChild {
//通配符检查
}
n.insertChild(path, fullPath, handlers)
return
}
//2.3 待插入节点和当前节点路径完全一致, 将handlers存到当前当前节点
if n.handlers != nil {
panic("handlers are already registered for path '" + fullPath + "'")
}
n.handlers = handlers
n.fullPath = fullPath
return
}
}
RouterGroup
- 用于配置请求的公共路由 ```go type RouterGroup struct { Handlers HandlersChain //中间件调用链 basePath string //公共路径 engine *Engine //egine引用 root bool }
//RouterGroup实现了IRouter接口, 提供如下方法 // IRouter defines all router handle interface includes single and group router. type IRouter interface { IRoutes Group(string, …HandlerFunc) *RouterGroup }
// IRoutes defines all router handle interface. type IRoutes interface { Use(…HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
StaticFile(string, string) IRoutes
Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes
}
- 核心方法
- Group基于当前RouterGroup再创建出一个新的RouterGroup
```go
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}
Use给RouterGroup安装新的middlewares
// Use adds middleware to the group, see example code in GitHub.
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
handle所有方法注册最终都会调用该方法进行注册,内部最终调用Engine.addRoute添加路由
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
Context
Context是Gin框架自定义的,用于在调用链中传递请求参数和渲染响应数据等
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 protect Keys map
mu sync.RWMutex
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]interface{}
// 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 use url.ParseQuery cached the param query result from c.Request.URL.Query()
queryCache url.Values
// formCache use url.ParseQuery cached PostForm 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
}