上一篇學習了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的主要流程和主體思路。學習