From c44f0625dc3efff6a86a580a3e28fe5db74679af Mon Sep 17 00:00:00 2001 From: gzdaijie Date: Sat, 17 Aug 2019 01:47:14 +0800 Subject: [PATCH] add day5 middleware --- README.md | 40 ++++++++++++- day2-context/gee/context.go | 19 +++--- day3-router/gee/context.go | 21 ++++--- day3-router/main.go | 2 +- day4-group/gee/context.go | 21 ++++--- day4-group/gee/gee.go | 16 ++++-- day4-group/main.go | 2 +- day5-middleware/gee/context.go | 92 ++++++++++++++++++++++++++++++ day5-middleware/gee/gee.go | 82 ++++++++++++++++++++++++++ day5-middleware/gee/logger.go | 17 ++++++ day5-middleware/gee/router.go | 89 +++++++++++++++++++++++++++++ day5-middleware/gee/router_test.go | 47 +++++++++++++++ day5-middleware/gee/trie.go | 80 ++++++++++++++++++++++++++ day5-middleware/main.go | 48 ++++++++++++++++ doc/gee-day1.md | 10 ++-- doc/gee.md | 19 +++--- 16 files changed, 558 insertions(+), 47 deletions(-) create mode 100644 day5-middleware/gee/context.go create mode 100644 day5-middleware/gee/gee.go create mode 100644 day5-middleware/gee/logger.go create mode 100644 day5-middleware/gee/router.go create mode 100644 day5-middleware/gee/router_test.go create mode 100644 day5-middleware/gee/trie.go create mode 100644 day5-middleware/main.go diff --git a/README.md b/README.md index ba26acb..c1d4d96 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -# # 7天用Go从零实现Web框架Gee +# 7天用Go从零实现Web框架Gee ![Gee](doc/gee/gee.jpg) +Gee 的设计与实现参考了Gin,这个教程可以快速入门:[Go Gin简明教程](https://geektutu.com/post/quick-go-gin.html)。 + ## [教程目录](https://geektutu.com/post/gee.html) - [第一天:前置知识(http.Handler接口)](https://geektutu.com/post/gee-day1.html),[Code - Github](day1-http-base) - 第二天:Context上下文设计,[Code - Github](day2-context) - 第三天:Tire树路由(Router),[Code - Github](day3-router) - 第四天:分组控制(Group),[Code - Github](day4-group) -- 第五天:中间件(Middleware) +- 第五天:中间件(Middleware),[Code - Github](day5-middleware) - 第六天:HTML模板(Template) - 第七天:异常错误处理(Panic) @@ -119,4 +121,38 @@ func main() { r.Run(":9999") } +``` + +## Day 5 - Middleware + +```go +func onlyForV2() gee.HandlerFunc { + return func(c *gee.Context) { + // Start timer + t := time.Now() + // Process request + c.Next() + // Calculate resolution time + log.Printf("[%d] %s in %v for group v2", c.StatusCode, c.Req.RequestURI, time.Since(t)) + } +} + +func main() { + r := gee.New() + r.Use(gee.Logger()) // global midlleware + r.GET("/", func(c *gee.Context) { + c.HTML(http.StatusOK, "

Hello Gee

") + }) + + v2 := r.Group("/v2") + v2.Use(onlyForV2()) // v2 group middleware + { + v2.GET("/hello/:name", func(c *gee.Context) { + // expect /hello/geektutu + c.String(http.StatusOK, "hello %s, you're at %s\n", c.Param("name"), c.Path) + }) + } + + r.Run(":9999") +} ``` \ No newline at end of file diff --git a/day2-context/gee/context.go b/day2-context/gee/context.go index 834b32a..41309f5 100644 --- a/day2-context/gee/context.go +++ b/day2-context/gee/context.go @@ -7,18 +7,22 @@ import ( ) type Context struct { - Path string - Method string + // origin objects Writer http.ResponseWriter Req *http.Request + // request info + Path string + Method string + // response info + StatusCode int } func newContext(w http.ResponseWriter, req *http.Request) *Context { return &Context{ + Writer: w, + Req: req, Path: req.URL.Path, Method: req.Method, - Req: req, - Writer: w, } } @@ -31,6 +35,7 @@ func (c *Context) Query(key string) string { } func (c *Context) Status(code int) { + c.StatusCode = code c.Writer.WriteHeader(code) } @@ -45,13 +50,13 @@ func (c *Context) String(code int, format string, values ...interface{}) { } func (c *Context) HTML(code int, html string) { - c.Writer.WriteHeader(code) + c.Status(code) c.SetHeader("Content-Type", "text/html") c.Writer.Write([]byte(html)) } func (c *Context) JSON(code int, obj interface{}) { - c.Writer.WriteHeader(code) + c.Status(code) c.SetHeader("Content-Type", "application/json") encoder := json.NewEncoder(c.Writer) if err := encoder.Encode(obj); err != nil { @@ -60,6 +65,6 @@ func (c *Context) JSON(code int, obj interface{}) { } func (c *Context) Data(code int, data []byte) { - c.Writer.WriteHeader(code) + c.Status(code) c.Writer.Write(data) } diff --git a/day3-router/gee/context.go b/day3-router/gee/context.go index 44139e5..08d6048 100644 --- a/day3-router/gee/context.go +++ b/day3-router/gee/context.go @@ -7,20 +7,24 @@ import ( ) type Context struct { - Path string - Method string + // origin objects Writer http.ResponseWriter Req *http.Request + // request info + Path string + Method string Params map[string]string + // response info + StatusCode int } func newContext(w http.ResponseWriter, req *http.Request, params map[string]string) *Context { return &Context{ + Writer: w, + Req: req, Path: req.URL.Path, Method: req.Method, - Params: make(map[string]string), - Req: req, - Writer: w, + Params: params, } } @@ -38,6 +42,7 @@ func (c *Context) Query(key string) string { } func (c *Context) Status(code int) { + c.StatusCode = code c.Writer.WriteHeader(code) } @@ -52,13 +57,13 @@ func (c *Context) String(code int, format string, values ...interface{}) { } func (c *Context) HTML(code int, html string) { - c.Writer.WriteHeader(code) + c.Status(code) c.SetHeader("Content-Type", "text/html") c.Writer.Write([]byte(html)) } func (c *Context) JSON(code int, obj interface{}) { - c.Writer.WriteHeader(code) + c.Status(code) c.SetHeader("Content-Type", "application/json") encoder := json.NewEncoder(c.Writer) if err := encoder.Encode(obj); err != nil { @@ -67,6 +72,6 @@ func (c *Context) JSON(code int, obj interface{}) { } func (c *Context) Data(code int, data []byte) { - c.Writer.WriteHeader(code) + c.Status(code) c.Writer.Write(data) } diff --git a/day3-router/main.go b/day3-router/main.go index bf5874b..349c289 100644 --- a/day3-router/main.go +++ b/day3-router/main.go @@ -14,7 +14,7 @@ package main // (3) // $ curl "http://localhost:9999/hello/geektutu" -// hello , you're at /hello/geektutu +// hello geektutu, you're at /hello/geektutu // (4) // $ curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' diff --git a/day4-group/gee/context.go b/day4-group/gee/context.go index 44139e5..08d6048 100644 --- a/day4-group/gee/context.go +++ b/day4-group/gee/context.go @@ -7,20 +7,24 @@ import ( ) type Context struct { - Path string - Method string + // origin objects Writer http.ResponseWriter Req *http.Request + // request info + Path string + Method string Params map[string]string + // response info + StatusCode int } func newContext(w http.ResponseWriter, req *http.Request, params map[string]string) *Context { return &Context{ + Writer: w, + Req: req, Path: req.URL.Path, Method: req.Method, - Params: make(map[string]string), - Req: req, - Writer: w, + Params: params, } } @@ -38,6 +42,7 @@ func (c *Context) Query(key string) string { } func (c *Context) Status(code int) { + c.StatusCode = code c.Writer.WriteHeader(code) } @@ -52,13 +57,13 @@ func (c *Context) String(code int, format string, values ...interface{}) { } func (c *Context) HTML(code int, html string) { - c.Writer.WriteHeader(code) + c.Status(code) c.SetHeader("Content-Type", "text/html") c.Writer.Write([]byte(html)) } func (c *Context) JSON(code int, obj interface{}) { - c.Writer.WriteHeader(code) + c.Status(code) c.SetHeader("Content-Type", "application/json") encoder := json.NewEncoder(c.Writer) if err := encoder.Encode(obj); err != nil { @@ -67,6 +72,6 @@ func (c *Context) JSON(code int, obj interface{}) { } func (c *Context) Data(code int, data []byte) { - c.Writer.WriteHeader(code) + c.Status(code) c.Writer.Write(data) } diff --git a/day4-group/gee/gee.go b/day4-group/gee/gee.go index a426642..35c1299 100644 --- a/day4-group/gee/gee.go +++ b/day4-group/gee/gee.go @@ -10,14 +10,16 @@ type HandlerFunc func(*Context) // Engine implement the interface of ServeHTTP type ( RouterGroup struct { - prefix string - parent *RouterGroup // support nesting - engine *Engine // all groups share a Engine instance + prefix string + middlewares []HandlerFunc // support middleware + parent *RouterGroup // support nesting + engine *Engine // all groups share a Engine instance } Engine struct { *RouterGroup router *router + groups []*RouterGroup // store all group } ) @@ -25,17 +27,21 @@ type ( func New() *Engine { engine := &Engine{router: newRouter()} engine.RouterGroup = &RouterGroup{engine: engine} + engine.groups = []*RouterGroup{engine.RouterGroup} return engine } // Group is defined to create a new RouterGroup // remember all groups share the same Engine instance func (group *RouterGroup) Group(prefix string) *RouterGroup { - return &RouterGroup{ + engine := group.engine + newGroup := &RouterGroup{ prefix: group.prefix + prefix, parent: group, - engine: group.engine, + engine: engine, } + engine.groups = append(engine.groups, newGroup) + return newGroup } func (group *RouterGroup) addRoute(method string, comp string, handler HandlerFunc) { diff --git a/day4-group/main.go b/day4-group/main.go index 37c78da..bab7c2e 100644 --- a/day4-group/main.go +++ b/day4-group/main.go @@ -14,7 +14,7 @@ package main // (3) // $ curl "http://localhost:9999/v2/hello/geektutu" -// hello , you're at /v2/hello/geektutu +// hello geektutu, you're at /hello/geektutu // (4) // $ curl "http://localhost:9999/v2/login" -X POST -d 'username=geektutu&password=1234' diff --git a/day5-middleware/gee/context.go b/day5-middleware/gee/context.go new file mode 100644 index 0000000..8ba021e --- /dev/null +++ b/day5-middleware/gee/context.go @@ -0,0 +1,92 @@ +package gee + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type Context struct { + // origin objects + Writer http.ResponseWriter + Req *http.Request + // request info + Path string + Method string + Params map[string]string + // response info + StatusCode int + // middleware + handlers []HandlerFunc + index int +} + +func newContext(w http.ResponseWriter, req *http.Request, + params map[string]string, handlers []HandlerFunc) *Context { + + return &Context{ + Path: req.URL.Path, + Method: req.Method, + Params: params, + Req: req, + Writer: w, + handlers: handlers, + index: -1, + } +} + +func (c *Context) Next() { + c.index++ + s := len(c.handlers) + for ; c.index < s; c.index++ { + c.handlers[c.index](c) + } +} + +func (c *Context) Param(key string) string { + value, _ := c.Params[key] + return value +} + +func (c *Context) PostForm(key string) string { + return c.Req.FormValue(key) +} + +func (c *Context) Query(key string) string { + return c.Req.URL.Query().Get(key) +} + +func (c *Context) Status(code int) { + c.StatusCode = code + c.Writer.WriteHeader(code) +} + +func (c *Context) SetHeader(key string, value string) { + c.Writer.Header().Set(key, value) +} + +func (c *Context) String(code int, format string, values ...interface{}) { + c.Status(code) + c.SetHeader("Content-Type", "text/plain") + c.Writer.Write([]byte(fmt.Sprintf(format, values...))) +} + +func (c *Context) HTML(code int, html string) { + c.Status(code) + c.SetHeader("Content-Type", "text/html") + c.Writer.Write([]byte(html)) +} + +func (c *Context) JSON(code int, obj interface{}) { + c.Status(code) + c.SetHeader("Content-Type", "application/json") + encoder := json.NewEncoder(c.Writer) + if err := encoder.Encode(obj); err != nil { + http.Error(c.Writer, err.Error(), 500) + } +} + +func (c *Context) Data(code int, data []byte) { + c.Status(code) + c.Writer.Write(data) +} diff --git a/day5-middleware/gee/gee.go b/day5-middleware/gee/gee.go new file mode 100644 index 0000000..3ba8446 --- /dev/null +++ b/day5-middleware/gee/gee.go @@ -0,0 +1,82 @@ +package gee + +import ( + "net/http" + "strings" +) + +// HandlerFunc defines the request handler used by gee +type HandlerFunc func(*Context) + +// Engine implement the interface of ServeHTTP +type ( + RouterGroup struct { + prefix string + middlewares []HandlerFunc // support middleware + parent *RouterGroup // support nesting + engine *Engine // all groups share a Engine instance + } + + Engine struct { + *RouterGroup + router *router + groups []*RouterGroup // store all group + } +) + +// New is the constructor of gee.Engine +func New() *Engine { + engine := &Engine{router: newRouter()} + engine.RouterGroup = &RouterGroup{engine: engine} + engine.groups = []*RouterGroup{engine.RouterGroup} + return engine +} + +// Group is defined to create a new RouterGroup +// remember all groups share the same 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 +} + +// Use is defined to add middleware to the group +func (group *RouterGroup) Use(middlewares ...HandlerFunc) { + group.middlewares = append(group.middlewares, middlewares...) +} + +func (group *RouterGroup) addRoute(method string, comp string, handler HandlerFunc) { + pattern := group.prefix + comp + + group.engine.router.addRoute(method, pattern, handler) +} + +// 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) +} + +// Run defines the method to start a http server +func (engine *Engine) Run(addr string) (err error) { + return http.ListenAndServe(addr, engine) +} + +func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { + var middlewares []HandlerFunc + for _, group := range engine.groups { + if strings.HasPrefix(req.URL.Path, group.prefix) { + middlewares = append(middlewares, group.middlewares...) + } + } + engine.router.handle(w, req, middlewares) +} diff --git a/day5-middleware/gee/logger.go b/day5-middleware/gee/logger.go new file mode 100644 index 0000000..a4df935 --- /dev/null +++ b/day5-middleware/gee/logger.go @@ -0,0 +1,17 @@ +package gee + +import ( + "log" + "time" +) + +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.StatusCode, c.Req.RequestURI, time.Since(t)) + } +} diff --git a/day5-middleware/gee/router.go b/day5-middleware/gee/router.go new file mode 100644 index 0000000..c87070f --- /dev/null +++ b/day5-middleware/gee/router.go @@ -0,0 +1,89 @@ +package gee + +import ( + "net/http" + "strings" +) + +type router struct { + roots map[string]*node + handlers map[string]HandlerFunc +} + +func newRouter() *router { + return &router{ + roots: make(map[string]*node), + handlers: make(map[string]HandlerFunc), + } +} + +func (r *router) addRoute(method string, pattern string, handler HandlerFunc) { + parts := filterNonEmpty(strings.Split(pattern, "/")) + key := method + "-" + pattern + _, ok := r.roots[method] + if !ok { + r.roots[method] = &node{} + } + r.roots[method].insert(pattern, parts, 0) + r.handlers[key] = handler +} + +func (r *router) handle(w http.ResponseWriter, req *http.Request, middlewares []HandlerFunc) { + n, params := r.getRoute(req.Method, req.URL.Path) + handlers := middlewares + + if n != nil { + key := req.Method + "-" + n.pattern + handlers = append(middlewares, r.handlers[key]) + } else { + handlers = append(middlewares, func(c *Context) { + c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path) + }) + } + c := newContext(w, req, params, handlers) + c.Next() +} + +func (r *router) getRoute(method string, pattern string) (*node, map[string]string) { + searchParts := filterNonEmpty(strings.Split(pattern, "/")) + params := make(map[string]string) + root, ok := r.roots[method] + + if !ok { + return nil, nil + } + + n := root.search(searchParts, 0) + + if n != nil { + parts := filterNonEmpty(strings.Split(n.pattern, "/")) + for index, part := range parts { + if part[0] == ':' { + params[part[1:]] = searchParts[index] + } + } + return n, params + } + + return nil, nil +} + +func (r *router) getRoutes(method string) []*node { + root, ok := r.roots[method] + if !ok { + return nil + } + nodes := make([]*node, 0) + root.travel(&nodes) + return nodes +} + +func filterNonEmpty(vs []string) []string { + parts := make([]string, 0) + for _, item := range vs { + if item != "" { + parts = append(parts, item) + } + } + return parts +} diff --git a/day5-middleware/gee/router_test.go b/day5-middleware/gee/router_test.go new file mode 100644 index 0000000..76c81b2 --- /dev/null +++ b/day5-middleware/gee/router_test.go @@ -0,0 +1,47 @@ +package gee + +import ( + "fmt" + "testing" +) + +func newTestRouter() *router { + r := newRouter() + r.addRoute("GET", "/", nil) + r.addRoute("GET", "/hello/:name", nil) + r.addRoute("GET", "/hello/b/c", nil) + r.addRoute("GET", "/hi/:name", nil) + return r +} + +func TestGetRoute(t *testing.T) { + r := newTestRouter() + n, ps := r.getRoute("GET", "/hello/geektutu") + + if n == nil { + t.Fatal("nil shouldn't be returned") + } + + if n.pattern != "/hello/:name" { + t.Fatal("should match /hello/:name") + } + + if ps["name"] != "geektutu" { + t.Fatal("name should be equal to 'geektutu'") + } + + fmt.Printf("matched path: %s, params['name']: %s\n", n.pattern, ps["name"]) + +} + +func TestGetRoutes(t *testing.T) { + r := newTestRouter() + nodes := r.getRoutes("GET") + for i, n := range nodes { + fmt.Println(i+1, n) + } + + if len(nodes) != 4 { + t.Fatal("the number of routes shoule be 4") + } +} diff --git a/day5-middleware/gee/trie.go b/day5-middleware/gee/trie.go new file mode 100644 index 0000000..86697f0 --- /dev/null +++ b/day5-middleware/gee/trie.go @@ -0,0 +1,80 @@ +package gee + +import ( + "fmt" +) + +type node struct { + pattern string + part string + children []*node + isWild bool +} + +func (n *node) String() string { + return fmt.Sprintf("node{pattern=%s, part=%s, isWild=%t}", n.pattern, n.part, n.isWild) +} + +func (n *node) insert(pattern string, parts []string, height int) { + if len(parts) == height { + n.pattern = pattern + return + } + + part := parts[height] + child := n.matchChild(part) + if child == nil { + child = &node{part: part, isWild: part[0] == ':'} + n.children = append(n.children, child) + } + child.insert(pattern, parts, height+1) +} + +func (n *node) search(parts []string, height int) *node { + if len(parts) == height { + if n.pattern == "" { + return nil + } + return n + } + + part := parts[height] + children := n.matchChildren(part) + + for _, child := range children { + result := child.search(parts, height+1) + if result != nil { + return result + } + } + + return nil +} + +func (n *node) travel(list *([]*node)) { + if n.pattern != "" { + *list = append(*list, n) + } + for _, child := range n.children { + child.travel(list) + } +} + +func (n *node) matchChild(part string) *node { + for _, child := range n.children { + if child.part == part || child.isWild { + return child + } + } + return nil +} + +func (n *node) matchChildren(part string) []*node { + nodes := make([]*node, 0) + for _, child := range n.children { + if child.part == part || child.isWild { + nodes = append(nodes, child) + } + } + return nodes +} diff --git a/day5-middleware/main.go b/day5-middleware/main.go new file mode 100644 index 0000000..2d1d5e0 --- /dev/null +++ b/day5-middleware/main.go @@ -0,0 +1,48 @@ +package main + +// (1) global middleware Logger +// $ curl -i http://localhost:9999/ +// 2019/08/17 01:37:38 [200] / in 3.14µs + +// (2) global + group middleware +// $ curl http://localhost:9999/v2/hello/geektutu +// 2019/08/17 01:38:48 [200] /v2/hello/geektutu in 61.467µs for group v2 +// 2019/08/17 01:38:48 [200] /v2/hello/geektutu in 281µs + +import ( + "log" + "net/http" + "time" + + "./gee" +) + +func onlyForV2() gee.HandlerFunc { + return func(c *gee.Context) { + // Start timer + t := time.Now() + // Process request + c.Next() + // Calculate resolution time + log.Printf("[%d] %s in %v for group v2", c.StatusCode, c.Req.RequestURI, time.Since(t)) + } +} + +func main() { + r := gee.New() + r.Use(gee.Logger()) // global midlleware + r.GET("/", func(c *gee.Context) { + c.HTML(http.StatusOK, "

Hello Gee

") + }) + + v2 := r.Group("/v2") + v2.Use(onlyForV2()) // v2 group middleware + { + v2.GET("/hello/:name", func(c *gee.Context) { + // expect /hello/geektutu + c.String(http.StatusOK, "hello %s, you're at %s\n", c.Param("name"), c.Path) + }) + } + + r.Run(":9999") +} diff --git a/doc/gee-day1.md b/doc/gee-day1.md index 7b5b3c8..272bbc4 100644 --- a/doc/gee-day1.md +++ b/doc/gee-day1.md @@ -12,7 +12,7 @@ keywords: - 动手写Web框架 - net/http image: post/gee/gee.jpg -github: https://github.com/geektutu/7days-gee-golang-web +github: https://github.com/geektutu/7days-golang --- 本文是 [7天用Go从零实现Web框架Gee教程系列](https://geektutu.com/post/gee.html)的第一篇。 @@ -24,7 +24,7 @@ github: https://github.com/geektutu/7days-gee-golang-web Go语言内置了 `net/http`库,封装了HTTP网络编程的基础的接口,我们实现的`Gee` Web 框架便是基于`net/http`的。我们接下来通过一个例子,简单介绍下这个库的使用。 -**[day1-http-base/base1/main.go](https://github.com/geektutu/7days-gee-golang-web/tree/master/day1-http-base/base1)** +**[day1-http-base/base1/main.go](https://github.com/geektutu/7days-golang/tree/master/day1-http-base/base1)** ```go package main @@ -82,7 +82,7 @@ func ListenAndServe(address string, h Handler) error 第二个参数的类型是什么呢?通过查看`net/http`的源码可以发现,`Handler`是一个接口,需要实现方法 _ServeHTTP_ ,也就是说,只要传入任何实现了 _ServerHTTP_ 接口的实例,所有的HTTP请求,就都交给了该实例处理了。马上来试一试吧。 -**[day1-http-base/base2/main.go](https://github.com/geektutu/7days-gee-golang-web/tree/master/day1-http-base/base2)** +**[day1-http-base/base2/main.go](https://github.com/geektutu/7days-golang/tree/master/day1-http-base/base2)** ```go package main @@ -135,7 +135,7 @@ main.go ### main.go -**[day1-http-base/base3/main.go](https://github.com/geektutu/7days-gee-golang-web/tree/master/day1-http-base/base3)** +**[day1-http-base/base3/main.go](https://github.com/geektutu/7days-golang/tree/master/day1-http-base/base3)** ```go package main @@ -167,7 +167,7 @@ func main() { ### gee.go -**[day1-http-base/base3/gee/gee.go](https://github.com/geektutu/7days-gee-golang-web/tree/master/day1-http-base/base3)** +**[day1-http-base/base3/gee/gee.go](https://github.com/geektutu/7days-golang/tree/master/day1-http-base/base3)** ```go package gee diff --git a/doc/gee.md b/doc/gee.md index 7e57908..8defd98 100644 --- a/doc/gee.md +++ b/doc/gee.md @@ -12,7 +12,7 @@ keywords: - 动手写 - from scratch image: post/gee/gee.jpg -github: https://github.com/geektutu/7days-gee-golang-web +github: https://github.com/geektutu/7days-golang --- ![gee](gee/gee.jpg) @@ -58,15 +58,14 @@ func handler(w http.ResponseWriter, r *http.Request) { 时间关系,同时为了尽可能地简洁明了,这个框架中的很多部分实现的功能都很简单,但是尽可能地体现一个框架核心的设计原则。例如`Router`的设计,虽然支持的动态路由规则有限,但为了性能考虑匹配算法是用`Trie树`实现的,`Router`最重要的指标之一便是性能。 -希望这个教程能够对你有所启发,如果对 Gee 有任何好的建议,欢迎提[issues - Github](https://github.com/geektutu/7days-gee-golang-web/issues) 和 PR。教程中的任何问题,可以直接在文章末尾评论。 +希望这个教程能够对你有所启发,如果对 Gee 有任何好的建议,欢迎提[issues - Github](https://github.com/geektutu/7days-golang/issues) 和 PR。教程中的任何问题,可以直接在文章末尾评论。 ## 目录 -- [第一天:前置知识(http.Handler接口)](https://geektutu.com/post/gee-day1.html),[Code - Github](https://github.com/geektutu/7days-gee-golang-web/tree/master/day1-http-base) -- 第二天:Tire树实现路由(Router) -- 第三天:设计Context -- 第四天:支持模板(HTML Template) -- 第五天:支持中间件(Middleware) -- 第六天:简单鉴权(BASIC AUTH) -- 第七天:异常错误处理(Panic) - +- [第一天:前置知识(http.Handler接口)](https://geektutu.com/post/gee-day1.html),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day1-http-base) +- 第二天:Context上下文设计,[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day2-context) +- 第三天:Tire树路由(Router),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day3-router) +- 第四天:分组控制(Group),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day4-group) +- 第五天:中间件(Middleware) +- 第六天:HTML模板(Template) +- 第七天:异常错误处理(Panic) \ No newline at end of file