在本节,最终的代码目录结构如下:
1 2 3 4 5 6 7 8 9
| dain/ |--context.go |--dain.go |--logger.go |--router.go |--trie.go |--go.mod main.go go.mod
|
实现目标
实现中间件的添加以及 Logger 中间件(用于记录请求处理时间和响应码)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package main
import ( "DawnGin/dain" "net/http" )
func main() { e := dain.New()
e.Use(dain.Logger())
e.Get("/hello/:name", func(c *dain.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello, you are %v, URL path = %v", name, c.Path) })
v1 := e.Group("/v1") { v1.Get("/video/:name", func(c *dain.Context) { videoName := c.Param("name") c.String(http.StatusOK, "Hello, this is v1 group, video name = %v, path = %v", videoName, c.Path) }) }
e.Run(":9000") }
|
中间件
Context
dain/context
在上下文 Context 中需要保存中间件信息,以及需要保存执行到第几个中间件了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| type Context struct { Writer http.ResponseWriter Req *http.Request Path string Method string Params map[string]string StatusCode int handlers []HandlerFunc index int }
func NewContext(w http.ResponseWriter, r *http.Request) *Context { return &Context{ Writer: w, Req: r, Path: r.URL.Path, Method: r.Method, index: -1, } }
|
c.Next
方法可以执行下一个中间件,实现在执行下一个中间件之后再进行一些额外的操作:
1 2 3 4 5 6 7
| func (c *Context) Next() { c.index++ for ; c.index < len(c.handlers); c.index++ { c.handlers[c.index](c) } }
|
将中间件应用到 Group
dain/dain.go
定义 Use 函数,用于将中间件添加到 Group 中。
1 2 3 4
| func (group *RouterGroup) Use(middlewares ...HandlerFunc) { group.middleware = append(group.middleware, middlewares...) }
|
同时,需要重写 ServeHTTP 函数,当我们接收到一个具体请求时,要判断该请求适用于哪些中间件,在这里我们简单通过 URL 的前缀来判断,将对应的中间件加入到 context 中。
1 2 3 4 5 6 7 8 9 10
| func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) { c := NewContext(w, r) for _, group := range e.groups { if strings.HasPrefix(r.URL.Path, group.prefix) { c.handlers = append(c.handlers, group.middleware...) } } e.router.handle(c) }
|
修改 router
dain/router.go
需要修改 router.handle 方法,将最后的请求处理逻辑 Handler 添加在中间件的最后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| func (r *router) handle(c *Context) { n, params := r.getRoute(c.Method, c.Path)
if n != nil { key := c.Method + "-" + n.pattern c.Params = params c.handlers = append(c.handlers, r.handlers[key]) } else { c.handlers = append(c.handlers, func(c *Context) { c.String(http.StatusNotFound, "404 NOT FOUND FOR PATH: %v", c.Path) }) }
c.Next() }
|
Logger 中间件
dain/logger.go
预定义一个 Logger 中间件,用于记录每一个请求的处理时间、响应码。
1 2 3 4 5 6 7 8 9 10
| func Logger() HandlerFunc { return func(c *Context) { startTime := time.Now() c.Next() log.Printf("[%d] %s in %v", c.StatusCode, c.Req.RequestURI, time.Since(startTime)) } }
|