教你如何搭建自己的go-gin框架(五 路由支持中间件)

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

## 引言 > 简单说下本章的重点 * 1、路由支持中间件 * 2、新增日志中间件 * 4、新增异常错误中间件 * 3、[代码地址 https://github.com/18211167516/go-Ebb/tree/master/day5-middleware](https://github.com/18211167516/go-Ebb/tree/master/day5-middleware) ## 1、重点 * 中间件的目的是为了让web框架灵活支持用户某些自定义行为,对外开放一个接口 * 核心的点在什么时候插入、以及中间件传入的支持参数 ## 2、思路 * 1、作用于每一条路由规则 * 2、作用于Context、依次执行该路由说匹配的中间件 * 3、中间件也和控制器层一样传入Context、接入点是框架接收到请求初始化Context对象后,允许用户使用自己定义的中间件做一些额外的处理、另外通过调用(*Context).Next()函数,中间件可等待用户自己定义的 Handler处理结束后,再做一些额外的操作 ## 3、Context支持 ```golang package ebb import ( "net/http" "fmt" "encoding/json" ) type Context struct{ //write and request Writer http.ResponseWriter Request *http.Request //request info Method string Path string Params map[string]interface{} HttpCode int //middleware 新增 handlers HandlersChain index int } func newContext(w http.ResponseWriter,r *http.Request) *Context{ context := &Context{ Writer:w, Request:r, Path: r.URL.Path, Method: r.Method, Params: make(map[string]interface{}), index: -1, } return context } //本次新增 func (c *Context) Next() { c.index++ for c.index < len(c.handlers) { c.handlers[c.index](c) c.index++ } } ``` ## 4、routerGroup支持 ```golang package ebb type HandlersChain []HandlerFunc //新增 type RouterGroup struct { prefix string middlewares HandlersChain // support middleware parent *RouterGroup // support nesting engine *Engine // all groups share a Engine instance } func (group *RouterGroup) Group(prefix string) *RouterGroup { engine := group.engine newGroup := &RouterGroup{ prefix: group.prefix + prefix,//支持分组嵌套 parent: group, engine: engine, } engine.groups = append(engine.groups, newGroup) return newGroup } //添加中间件 v5新增 func (group *RouterGroup) Use(middlewares ...HandlerFunc){ group.middlewares = append(group.middlewares, middlewares...) } //合并handlers func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { finalSize := len(group.middlewares) + len(handlers) mergedHandlers := make(HandlersChain, finalSize) copy(mergedHandlers, group.middlewares) copy(mergedHandlers[len(group.middlewares):], handlers) return mergedHandlers } func (group *RouterGroup) addRoute(method string, comp string, handler ...HandlerFunc) { pattern := group.prefix + comp //添加的路由的时候将中间件合并到 路由handles handlers := group.combineHandlers(handler) group.engine.router.addRoute(method, pattern, handlers) } // GET defines the method to add GET request func (group *RouterGroup) GET(pattern string, handler ...HandlerFunc) { group.addRoute("GET", pattern, handler...) } // POST defines the method to add POST request func (group *RouterGroup) POST(pattern string, handler ...HandlerFunc) { group.addRoute("POST", pattern, handler...) } ``` ## 5、路由支持 ```golang package ebb import ( "strings" ) type router struct{ roots map[string]*node handlers map[string]HandlersChain //新增 } func newRouter() *router{ return &router{ roots:make(map[string]*node), handlers:make(map[string]HandlersChain), } } func (r *router) addRoute(method string,pattern string,handlers HandlersChain){ parts := parsePattern(pattern) key := method + "-" + pattern _, ok := r.roots[method] if !ok { r.roots[method] = &node{} } r.roots[method].insert(pattern, parts, 0) r.handlers[key] = append(r.handlers[key],handlers...) //合并中间件和handlers } ``` ## 6、ebb支持 ```golang package ebb import ( "net/http" ) //定义框架请求处理方法 type HandlerFunc func(*Context) //核心结构体 type Engine struct{ *RouterGroup //v4新增 顶级路由组 router *router groups []*RouterGroup // v4新增 } //实例化结构体 func New() *Engine{ engine := &Engine{ router : newRouter(), } engine.RouterGroup = &RouterGroup{engine: engine} engine.groups = []*RouterGroup{engine.RouterGroup} return engine } //新增 func Default() *Engine{ engine := New() engine.Use(Logger(),Recovery()) return engine } //启动服务 func (engine *Engine) Run(addr string) (err error){ return http.ListenAndServe(addr,engine) } //engine 实现ServeHTTP接口(所有的请求都会走到这) //查找是否路由映射表存在,如果存在则调用,否则返回404 func (engine *Engine) ServeHTTP(w http.ResponseWriter,req *http.Request){ c := newContext(w, req) engine.handleHTTPRequest(c) } //v2 新增 func (engine *Engine) handleHTTPRequest(c *Context){ node,params := engine.router.getRoute(c.Method,c.Path) if node != nil { c.Params = params key := c.Method + "-" + node.pattern //赋值 c.handlers = engine.router.handlers[key] }else{ c.handlers = append(c.handlers, func(c *Context) { c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path) }) } //真正调度执行中间件以及具体handler c.Next() } ``` ## 7、新增logger中间件 ```golang package ebb import ( "time" "log" ) func Logger() HandlerFunc { return func(c *Context) { // Start timer t := time.Now() // Process request c.Next() // Calculate resolution time log.Printf("[%d] %s in %v", c.HttpCode, c.Request.RequestURI, time.Since(t)) } } ``` ## 8、新增错误异常中间件 ```golang package ebb import ( "fmt" "log" "net/http" "runtime" "strings" ) // print stack trace for debug func trace(message string) string { var pcs [32]uintptr n := runtime.Callers(3, pcs[:]) // skip first 3 caller var str strings.Builder str.WriteString(message + "\nTraceback:") for _, pc := range pcs[:n] { fn := runtime.FuncForPC(pc) file, line := fn.FileLine(pc) str.WriteString(fmt.Sprintf("\n\t%s:%d", file, line)) } return str.String() } func Recovery() HandlerFunc { return func(c *Context) { defer func() { if err := recover(); err != nil { message := fmt.Sprintf("%s", err) log.Printf("%s\n\n", trace(message)) c.JSON(http.StatusInternalServerError, "Internal Server Error") } }() c.Next() } } ``` ## 9、使用案例 ```golang package main import ( "ebb" ) func main(){ r := ebb.Default() r.GET("/panic",func(c *ebb.Context) { panic("err") }) r.POST("/login/*name",func(c *ebb.Context) { c.JSON(200, ebb.H{ "name": c.Param("name"), }) }) r.Run(":8080") } ``` > 到目前为止,框架的基本架构已确立,后续就是一些补充性的功能,以及优化点 > 看起来是不是和gin框架非常类似 > 下一章节我们实现对go模板的使用封装

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

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

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