内容简介:上一篇学习了Gin框架的总体流程,但是自己查看源代码中被许多的零散小功能影响了主体流程的研究。所以觉得可以模仿Gin框架,自己写一个最简单仅仅含有主流程的demo。可以让我们更加深入了解Gin自己也可以再模仿的过程中更加了解源码。这个demo是我自己对Gin初步理解之后模仿写的,其中只包含主要功能结果
开场
上一篇学习了Gin框架的总体流程,但是自己查看源代码中被许多的零散小功能影响了主体流程的研究。所以觉得可以模仿Gin框架,自己写一个最简单仅仅含有主流程的demo。可以让我们更加深入了解Gin自己也可以再模仿的过程中更加了解源码。
功能
这个demo是我自己对Gin初步理解之后模仿写的,其中只包含主要功能
- 创建路由
- 新建group
- 添加中间件
- 启动http服务,接受客户端请求并返回简单数据 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的主要流程和主体思路。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。