上一篇学习了Gin框架的整体流程,可是本身查看源代码中被许多的零散小功能影响了主体流程的研究。因此以为能够模仿Gin框架,本身写一个最简单仅仅含有主流程的demo。可让咱们更加深刻了解Gin本身也能够再模仿的过程当中更加了解源码。app
这个demo是我本身对Gin初步理解以后模仿写的,其中只包含主要功能框架
Demo中的路由并无使用Gin的tree只是简单的使用了map来实现,基数树是一个相对独立逻辑准备以后单独再学习函数
package mygin import ( "fmt" "net/http" "path" "sync" ) //上下文context 简单的他添加response request engine指针 isabort就能够支持最简单的流程 type Context struct { Request *http.Request ResponseWrite http.ResponseWriter engine *Engine isAbort bool } type HandlerFun func(ctx *Context) type HandlerList []HandlerFun type Engine struct { RouterGroup Handlers []HandlerFun router map[string]HandlerList pool sync.Pool } type Message struct { Message string } type IRouter interface { Use(...HandlerFun) IRouter GET(string, ...HandlerFun) IRouter Group(string, ...HandlerFun) *RouterGroup } type RouterGroup struct { Handlers []HandlerFun engine *Engine basePath string } func NewEngine()(*Engine){ en := new(Engine) en.router = make(map[string]HandlerList) en.pool.New = func() interface{} { return en.allocateContext() } en.RouterGroup = RouterGroup{ basePath:"/", Handlers:nil, engine:en, } return en } func (engine *Engine)Run(addr string)(err error){ fmt.Println("Listening and serving HTTP on", addr) err = http.ListenAndServe(addr, engine) return } //继承http包中的handler接口,在run中便可传入engine func (engine *Engine)ServeHTTP(w http.ResponseWriter, req *http.Request) { c := engine.pool.Get().(*Context) c.ResponseWrite = w c.Request = req engine.handleHTTPRequest(c) engine.pool.Put(c) } //客户端请求以后具体执行的函数 以前文章所说的获取全部handler 一个个执行 // 这里简单用了for循环 判断isabort属性来判断是否中止 func (engine *Engine) handleHTTPRequest(c *Context){ httpMethod := c.Request.Method path := c.Request.URL.Path if handlers,ok := engine.router[httpMethod + "^" + path];ok{ for _,fu := range handlers{ fu(c) if c.isAbort{ return } } } } func (engine *Engine) allocateContext() *Context{ return &Context{engine:engine} } func (engine *Engine)addRoute(httpMethod, absolutePath string, handlers HandlerList){ engine.router[httpMethod + "^" + absolutePath] = handlers } //添加group方法 设置group的basepath 和handler func (routerGroup *RouterGroup)Group(path string,handlers ...HandlerFun) *RouterGroup{ rg := RouterGroup{} rg.Handlers = routerGroup.CombineHandlers(handlers) rg.basePath = path rg.engine = routerGroup.engine return &rg } func (routerGroup *RouterGroup)Use(handlers ...HandlerFun) IRouter{ routerGroup.Handlers = append(routerGroup.Handlers, handlers...) return routerGroup } func (group *RouterGroup) calculateAbsolutePath(relativePath string) string { return joinPaths(group.basePath, relativePath) } func joinPaths(absolutePath, relativePath string) string { if relativePath == ""{ return absolutePath } finalPath := path.Join(absolutePath,relativePath) appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath)!='/' if appendSlash{ return finalPath + "/" } return finalPath } //工具方法 获取字符串最后一个字符 func lastChar(str string) uint8 { if str ==""{ panic("The length of the string can't be 0") } return str[len(str)-1] } //计算路径合并handler 而后添加到map中 func (group *RouterGroup)handle(httpMethod, relativePath string, handlers HandlerList) IRouter{ absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.CombineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group } //合并handler 以后返回 func (group *RouterGroup)CombineHandlers(handlers HandlerList)HandlerList{ finalSize := len(group.Handlers) + len(handlers) mergedHandler := make(HandlerList, finalSize) copy(mergedHandler, group.Handlers) copy(mergedHandler[len(group.Handlers):], handlers) return mergedHandler } //添加get method路由 func (group *RouterGroup)GET(path string, handlers ...HandlerFun)(IRouter){ group.handle("GET", path, handlers) return group }
func TestEngine_Run(t *testing.T) { router := NewEngine() router.GET("/test", func(ctx *Context) { fmt.Println("get request") //这边能够尝试拿一下参数 在gin中获取参数提供了不少的方法 //这些不是主流程就没有在这里体现 有兴趣能够看一下源码其实也没有想象中的复杂 //这边就先获取一下get参数 pm := ctx.Request.URL.Query() if v,ok := pm["id"];ok{ fmt.Println("request url", ctx.Request.URL.String()," parameter id value =",v) } ctx.ResponseWrite.WriteHeader(200) r := render.JSON{Data:"success"} r.WriteContentType(ctx.ResponseWrite) if err := r.Render(ctx.ResponseWrite); err != nil{ panic(err) } }) router.Run(":2222") }
结果工具
//在console中,获得了客户端的请求并打印参数 Listening and serving HTTP on :2222 get request request url /test?id=2 parameter id value = [2] //客户端请求以后获取到了 success 的返回值 http://localhost:2222/test?id=2 "success"
这个demo大概100多行代码,仅仅实现了Gin最最小的功能。还有大部分的以前提到的功能都未实现,可是只是这100多行代码就已经能看出Gin的主要流程和主体思路。学习