简介
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,因此它也是一个根路由,具有路由组的相关功能```gotype Engine struct {//实现路由功能, Engine即是根路由RouterGroupdelims render.DelimsHTMLRender render.HTMLRenderFuncMap template.FuncMapallNoRoute HandlersChainallNoMethod HandlersChainnoRoute HandlersChainnoMethod HandlersChain//pool是gin实现的对象池,针对Context等频繁创建的对象提供缓存功能pool sync.Pool//由url和调用链构成的radix tree, 用于路由请求trees methodTreesmaxParams uint16maxSections uint16trustedProxies []stringtrustedCIDRs []*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启动函数,开始监听并处理请求```gofunc (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) {//从对象池取一个Contextc := 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 treet := engine.treesfor 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.handlersc.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.allNoMethodserveError(c, http.StatusMethodNotAllowed, default405Body)return}}}c.handlers = engine.allNoRouteserveError(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 := pathn.priority++//1. 空树, 将节点添加到父节点的子节点中if len(n.path) == 0 && len(n.children) == 0 {n.insertChild(path, fullPath, handlers)n.nType = rootreturn}//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 = niln.wildChild = falsen.fullPath = fullPath[:parentFullPathIndex+i]}//2.2 待插入节点和当前节点存在部分公共前缀,需要确定待插入节点和当前节点的子节点//是否存在公共前缀, 若存在则取对应子节点继续执行walk过程; 否则直接将待插入节点//插入到当前节点的子节点中if i < len(path) {path = path[i:] //取待插入节点的非公共部分c := path[0]//2.2.1 检查是否和子节点存在公共前缀(首字母), 存在则取该子节点, 继续walkfor 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 = handlersn.fullPath = fullPathreturn}}
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) IRoutesAny(string, ...HandlerFunc) IRoutesGET(string, ...HandlerFunc) IRoutesPOST(string, ...HandlerFunc) IRoutesDELETE(string, ...HandlerFunc) IRoutesPATCH(string, ...HandlerFunc) IRoutesPUT(string, ...HandlerFunc) IRoutesOPTIONS(string, ...HandlerFunc) IRoutesHEAD(string, ...HandlerFunc) IRoutesStaticFile(string, string) IRoutesStatic(string, string) IRoutesStaticFS(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 responseWriterRequest *http.RequestWriter ResponseWriterParams Paramshandlers HandlersChainindex int8fullPath stringengine *Engineparams *ParamsskippedNodes *[]skippedNode// This mutex protect Keys mapmu 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}
