Go Gin源码学习(二)

panlei914 · · 785 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

### 开场 上一篇学习了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的主要流程和主体思路。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

785 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传