diff --git a/README.md b/README.md
index 4dd3322..4a9a58c 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@ Gee 的设计与实现参考了Gin,这个教程可以快速入门:[Go Gin简
- [第一天:前置知识(http.Handler接口)](https://geektutu.com/post/gee-day1.html),[Code - Github](day1-http-base)
- [第二天:上下文设计(Context)](https://geektutu.com/post/gee-day2.html),[Code - Github](day2-context)
- [第三天:Tire树路由(Router)](https://geektutu.com/post/gee-day3.html),[Code - Github](day3-router)
-- 第四天:分组控制(Group),[Code - Github](day4-group)
-- 第五天:中间件(Middleware),[Code - Github](day5-middleware)
+- [第四天:分组控制(Group)](https://geektutu.com/post/gee-day4.html),[Code - Github](day4-group)
+- [第五天:中间件(Middleware)]((https://geektutu.com/post/gee-day5.html),[Code - Github](day5-middleware)
- 第六天:HTML模板(Template),[Code - Github](day6-template)
- 第七天:错误恢复(Panic Recover),[Code - Github](day7-panic-recover)
diff --git a/day4-group/gee/gee.go b/day4-group/gee/gee.go
index b7ee570..8c5d374 100644
--- a/day4-group/gee/gee.go
+++ b/day4-group/gee/gee.go
@@ -19,7 +19,7 @@ type (
Engine struct {
*RouterGroup
router *router
- groups []*RouterGroup // store all group
+ groups []*RouterGroup // store all groups
}
)
diff --git a/day4-group/main.go b/day4-group/main.go
index 1bd5346..68c2d74 100644
--- a/day4-group/main.go
+++ b/day4-group/main.go
@@ -1,7 +1,15 @@
package main
/*
-(1) v1
+(1) index
+curl -i http://localhost:9999/index
+HTTP/1.1 200 OK
+Date: Sun, 01 Sep 2019 08:12:23 GMT
+Content-Length: 19
+Content-Type: text/html; charset=utf-8
+
Index Page
+
+(2) v1
$ curl -i http://localhost:9999/v1/
HTTP/1.1 200 OK
Date: Mon, 12 Aug 2019 18:11:07 GMT
@@ -9,19 +17,19 @@ Content-Length: 18
Content-Type: text/html; charset=utf-8
Hello Gee
-(2)
+(3)
$ curl "http://localhost:9999/v1/hello?name=geektutu"
hello geektutu, you're at /v1/hello
-(3)
+(4)
$ curl "http://localhost:9999/v2/hello/geektutu"
hello geektutu, you're at /hello/geektutu
-(4)
+(5)
$ curl "http://localhost:9999/v2/login" -X POST -d 'username=geektutu&password=1234'
{"password":"1234","username":"geektutu"}
-(5)
+(6)
$ curl "http://localhost:9999/hello"
404 NOT FOUND: /hello
*/
@@ -34,6 +42,9 @@ import (
func main() {
r := gee.New()
+ r.GET("/index", func(c *gee.Context) {
+ c.HTML(http.StatusOK, "Index Page
")
+ })
v1 := r.Group("/v1")
{
v1.GET("/", func(c *gee.Context) {
diff --git a/day5-middleware/gee/gee.go b/day5-middleware/gee/gee.go
index 29ae1d4..e880d19 100644
--- a/day5-middleware/gee/gee.go
+++ b/day5-middleware/gee/gee.go
@@ -20,7 +20,7 @@ type (
Engine struct {
*RouterGroup
router *router
- groups []*RouterGroup // store all group
+ groups []*RouterGroup // store all groups
}
)
diff --git a/day6-template/gee/gee.go b/day6-template/gee/gee.go
index 54af464..e48a496 100644
--- a/day6-template/gee/gee.go
+++ b/day6-template/gee/gee.go
@@ -22,7 +22,7 @@ type (
Engine struct {
*RouterGroup
router *router
- groups []*RouterGroup // store all group
+ groups []*RouterGroup // store all groups
htmlTemplates *template.Template // for html render
funcMap template.FuncMap // for html render
}
diff --git a/day7-panic-recover/gee/gee.go b/day7-panic-recover/gee/gee.go
index 99bcf37..0643fb2 100644
--- a/day7-panic-recover/gee/gee.go
+++ b/day7-panic-recover/gee/gee.go
@@ -22,7 +22,7 @@ type (
Engine struct {
*RouterGroup
router *router
- groups []*RouterGroup // store all group
+ groups []*RouterGroup // store all groups
htmlTemplates *template.Template // for html render
funcMap template.FuncMap // for html render
}
diff --git a/doc/gee-day1.md b/doc/gee-day1.md
index 272bbc4..14385d2 100644
--- a/doc/gee-day1.md
+++ b/doc/gee-day1.md
@@ -1,7 +1,7 @@
---
title: Go语言动手写Web框架 - Gee第一天 http.Handler
date: 2019-08-12 00:10:10
-description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,从零设计一个Web框架。本文介绍了Go标准库 net/http 和 http.Handler 接口的使用,拦截所有的 HTTP 请求,交给Gee框架处理。
+description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。本文介绍了Go标准库 net/http 和 http.Handler 接口的使用,拦截所有的 HTTP 请求,交给Gee框架处理。
tags:
- Go
categories:
diff --git a/doc/gee-day2.md b/doc/gee-day2.md
index e619d1b..e4b5f89 100644
--- a/doc/gee-day2.md
+++ b/doc/gee-day2.md
@@ -1,7 +1,7 @@
---
title: Go语言动手写Web框架 - Gee第二天 上下文Context
date: 2019-08-19 00:10:10
-description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,从零设计一个Web框架。本文介绍了请求上下文(Context)的设计理念,封装了返回JSON/String/Data/HTML等类型响应的方法。
+description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。本文介绍了请求上下文(Context)的设计理念,封装了返回JSON/String/Data/HTML等类型响应的方法。
tags:
- Go
categories:
diff --git a/doc/gee-day3.md b/doc/gee-day3.md
index e595fc6..f838554 100644
--- a/doc/gee-day3.md
+++ b/doc/gee-day3.md
@@ -1,7 +1,7 @@
---
-title: Go语言动手写Web框架 - Gee第三天 路由Router
+title: Go语言动手写Web框架 - Gee第三天 前缀树路由Router
date: 2019-08-28 00:10:10
-description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,从零设计一个Web框架。本文介绍了如何用 Trie 前缀树实现路由。支持简单的参数解析和通配符的场景。
+description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。本文介绍了如何用 Trie 前缀树实现路由 Route。支持简单的参数解析和通配符的场景。
tags:
- Go
categories:
@@ -15,6 +15,11 @@ image: post/gee-day3/trie_router.jpg
github: https://github.com/geektutu/7days-golang
---
+本文是 [7天用Go从零实现Web框架Gee教程系列](https://geektutu.com/post/gee.html)的第三篇。
+
+- 使用 Tire 树实现动态路由(dynamic route)解析。
+- 支持两种模式`:name`和`*filepath`,**代码约150行**。
+
## Trie 树简介
之前,我们用了一个非常简单的`map`结构存储了路由表,使用`map`存储键值对,索引非常高效,但是有一个弊端,键值对的存储的方式,只能用来索引静态路由。那如果我们想支持类似于`/hello/:name`这样的动态路由怎么办呢?所谓动态路由,即一条路由规则可以匹配某一类型而非某一条固定的路由。例如`/hello/:name`,可以匹配`/hello/geektutu`、`hello/jack`等。
diff --git a/doc/gee-day4.md b/doc/gee-day4.md
new file mode 100644
index 0000000..c6263f0
--- /dev/null
+++ b/doc/gee-day4.md
@@ -0,0 +1,164 @@
+---
+title: Go语言动手写Web框架 - Gee第四天 分组控制Group
+date: 2019-09-01 15:10:10
+description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。本文介绍了分组控制(Group Control)的意义,以及嵌套分组路由的实现。
+tags:
+- Go
+categories:
+- 从零实现
+keywords:
+- Go语言
+- 从零实现Web框架
+- 动手写Web框架
+- Group Control
+image: post/gee-day4/group.jpg
+github: https://github.com/geektutu/7days-golang
+---
+
+本文是 [7天用Go从零实现Web框架Gee教程系列](https://geektutu.com/post/gee.html)的第四篇。
+
+- 实现路由分组控制(Route Group Control),**代码约50行**
+
+## 分组的意义
+
+分组控制(Group Control)是 Web 框架应提供的基础功能之一。所谓分组,是指路由的分组。如果没有路由分组,我们需要针对每一个路由进行控制。但是真实的业务场景中,往往某一组路由需要相似的处理。例如:
+
+- 以`/post`开头的路由匿名可访问。
+- 以`/admin`开头的路由需要鉴权。
+- 以`/api`开头的路由是 RESTful 接口,可以对接第三方平台,需要三方平台鉴权。
+
+大部分情况下的路由分组,是以相同的前缀来区分的。因此,我们今天实现的分组控制也是以前缀来区分,并且支持分组的嵌套。例如`/post`是一个分组,`/post/a`和`/post/b`可以是该分组下的子分组。作用在`/post`分组上的中间件(middleware),也都会作用在子分组,子分组还可以应用自己特有的中间件。
+
+中间件可以给框架提供无限的扩展能力,应用在分组上,可以使得分组控制的收益更为明显,而不是共享相同的路由前缀这么简单。例如`/admin`的分组,可以应用鉴权中间件;`/`分组应用日志中间件,`/`是默认的最顶层的分组,也就意味着给所有的路由,即整个框架增加了记录日志的能力。
+
+提供扩展能力支持中间件的内容,我们将在下一节当中介绍。
+
+##分组嵌套
+
+一个 Group 对象需要具备哪些属性呢?首先是前缀(prefix),比如`/`,或者`/api`;要支持分组嵌套,那么需要知道当前分组的父亲(parent)是谁;当然了,按照我们一开始的分析,中间件是应用在分组上的,那还需要存储应用在该分组上的中间件(middlewares)。还记得,我们之前调用函数`(*Engine).addRoute()`来映射所有的路由规则和 Handler 。如果Group对象需要直接映射路由规则的话,比如我们想在使用框架时,这么调用:
+
+```go
+r := gee.New()
+v1 := r.Group("/v1")
+v1.GET("/", func(c *gee.Context) {
+ c.HTML(http.StatusOK, "Hello Gee
")
+})
+```
+
+那么Group对象,还需要有访问`Router`的能力,为了方便,我们可以在Group中,保存一个指针,指向`Engine`,整个框架的所有资源都是由`Engine`统一协调的,那么就可以通过`Engine`间接地访问各种接口了。
+
+所以,最后的 Group 的定义是这样的:
+
+**[day4-group/gee/gee.go](https://github.com/geektutu/7days-golang/tree/master/day4-group/gee)**
+
+```go
+RouterGroup struct {
+ prefix string
+ middlewares []HandlerFunc // support middleware
+ parent *RouterGroup // support nesting
+ engine *Engine // all groups share a Engine instance
+}
+```
+
+我们还可以进一步地抽象,将`Engine`作为最顶层的分组,也就是说`Engine`拥有`RouterGroup`所有的能力。
+
+```go
+Engine struct {
+ *RouterGroup
+ router *router
+ groups []*RouterGroup // store all groups
+}
+```
+
+那我们就可以将和路由有关的函数,都交给`RouterGroup`实现了。
+
+```go
+// 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
+}
+
+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)
+}
+```
+
+可以仔细观察下`addRoute`函数,调用了`group.engine.router.addRoute`来实现了路由的映射。由于`Engine`从某种意义上继承了`RouterGroup`的所有属性和方法,因为 (*Engine).engine 是指向自己的。这样实现,我们既可以像原来一样添加路由,也可以通过分组添加路由。
+
+## 使用 Demo
+
+测试框架的Demo就可以这样写了:
+
+```go
+func main() {
+ r := gee.New()
+ r.GET("/index", func(c *gee.Context) {
+ c.HTML(http.StatusOK, "Index Page
")
+ })
+ v1 := r.Group("/v1")
+ {
+ v1.GET("/", func(c *gee.Context) {
+ c.HTML(http.StatusOK, "Hello Gee
")
+ })
+
+ v1.GET("/hello", func(c *gee.Context) {
+ // expect /hello?name=geektutu
+ c.String(http.StatusOK, "hello %s, you're at %s\n", c.Query("name"), c.Path)
+ })
+ }
+ v2 := r.Group("/v2")
+ {
+ 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)
+ })
+ v2.POST("/login", func(c *gee.Context) {
+ c.JSON(http.StatusOK, gee.H{
+ "username": c.PostForm("username"),
+ "password": c.PostForm("password"),
+ })
+ })
+
+ }
+
+ r.Run(":9999")
+}
+```
+
+通过 curl 简单测试:
+
+```bash
+$ curl "http://localhost:9999/v1/hello?name=geektutu"
+hello geektutu, you're at /v1/hello
+
+$ curl "http://localhost:9999/v2/hello/geektutu"
+hello geektutu, you're at /hello/geektutu
+```
+
diff --git a/doc/gee-day4/group.jpg b/doc/gee-day4/group.jpg
new file mode 100644
index 0000000..fc74774
Binary files /dev/null and b/doc/gee-day4/group.jpg differ
diff --git a/doc/gee-day5.md b/doc/gee-day5.md
new file mode 100644
index 0000000..9ba1147
--- /dev/null
+++ b/doc/gee-day5.md
@@ -0,0 +1,225 @@
+---
+title: Go语言动手写Web框架 - Gee第四天 中间件Middleware
+date: 2019-09-01 20:10:10
+description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。本文介绍了如何为Web框架添加中间件的功能(middlewares)。
+tags:
+- Go
+categories:
+- 从零实现
+keywords:
+- Go语言
+- 从零实现Web框架
+- 动手写Web框架
+- Middlewares
+image: post/gee-day5/middleware.jpg
+github: https://github.com/geektutu/7days-golang
+---
+
+本文是 [7天用Go从零实现Web框架Gee教程系列](https://geektutu.com/post/gee.html)的第五篇。
+
+- 设计并实现 Web 框架的中间件(Middlewares)机制。
+- 实现通用的`Logger`中间件,能够记录请求到响应所花费的时间,**代码约50行**
+
+## 中间件是什么
+
+中间件(middlewares),简单说,就是非业务的技术类组件。Web 框架本身不可能去理解所有的业务,因而不可能实现所有的功能。因此,框架需要有一个插口,允许用户自己定义功能,嵌入到框架中,仿佛这个功能是框架原生支持的一样。因此,对中间件而言,需要考虑2个比较关键的点:
+
+- 插入点在哪?使用框架的人并不关心底层逻辑的具体实现,如果插入点太底层,中间件逻辑就会非常复杂。如果插入点离用户太近,那和用户直接定义一组函数,每次在 Handler 中手工调用没有多大的优势了。
+- 中间件的输入是什么?中间件的输入,决定了扩展能力。暴露的参数太少,用户发挥空间有限。
+
+那对于一个 Web 框架而言,中间件应该设计成什么样呢?接下来的实现,基本参考了 Gin 框架。
+
+## 中间件设计
+
+Gee 的中间件的定义与路由映射的 Handler 一致,处理的输入是`Context`对象。插入点是框架接收到请求初始化`Context`对象后,允许用户使用自己定义的中间件做一些额外的处理,例如记录日志等,以及对`Context`进行二次加工。另外通过调用`(*Context).Next()`函数,中间件可等待用户自己定义的 `Handler`处理结束后,再做一些额外的操作,例如计算本次处理所用时间等。即 Gee 的中间件支持用户在请求被处理的前后,做一些额外的操作。举个例子,我们希望最终能够支持如下定义的中间件,`c.Next()`表示等待执行其他的中间件或用户的`Handler`:
+
+****[day4-group/gee/logger.go](https://github.com/geektutu/7days-golang/tree/master/day5-middleware/gee)****
+
+```go
+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))
+ }
+}
+```
+
+另外,支持设置多个中间件,依次进行调用。
+
+我们上一篇文章[分组控制 Group Control](https://geektutu.com/post/gee-day4.html)中讲到,中间件是应用在`RouterGroup`上的,应用在最顶层的 Group,相当于作用于全局,所有的请求都会被中间件处理。那为什么不作用在每一条路由规则上呢?作用在某条路由规则,那还不如用户直接在 Handler 中调用直观。只作用在某条路由规则的功能通用性太差,不适合定义为中间件。
+
+我们之前的框架设计是这样的,当接收到请求后,匹配路由,该请求的所有信息都保存在`Context`中。中间件也不例外,接收到请求后,应查找所有应作用于该路由的中间件,保存在`Context`中,依次进行调用。为什么依次调用后,还需要在`Context`中保存呢?因为在设计中,中间件不仅作用在处理流程前,也可以作用在处理流程后,即在用户定义的 Handler 处理完毕后,还可以执行剩下的操作。
+
+为此,我们给`Context`添加了2个参数,定义了`Next`方法:
+
+**[day4-group/gee/context.go](https://github.com/geektutu/7days-golang/tree/master/day5-middleware/gee)**
+
+```go
+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) *Context {
+ return &Context{
+ Path: req.URL.Path,
+ Method: req.Method,
+ Req: req,
+ Writer: w,
+ index: -1,
+ }
+}
+
+func (c *Context) Next() {
+ c.index++
+ s := len(c.handlers)
+ for ; c.index < s; c.index++ {
+ c.handlers[c.index](c)
+ }
+}
+```
+
+`index`是记录当前执行到第几个中间件,当在中间件中调用`Next`方法时,控制权交给了下一个中间件,直到调用到最后一个中间件,然后再从后往前,调用每个中间件在`Next`方法之后定义的部分。如果我们将用户在映射路由时定义的`Handler`添加到`c.handlers`列表中,结果会怎么样呢?想必你已经猜到了。
+
+
+```
+func A(c *Context) {
+ part1
+ c.Next()
+ part2
+}
+func B(c *Context) {
+ part3
+ c.Next()
+ part4
+}
+```
+
+假设我们应用了中间件 A 和 B,和路由映射的 Handler。`c.handlers`是这样的[A, B, Handler],`c.index`初始化为-1。调用`c.Next()`,接下来的流程是这样的:
+
+- c.index++,c.index 变为 0
+- 0 < 3,调用 c.handlers[0],即 A
+- 执行 part1,调用 c.Next()
+- c.index++,c.index 变为 1
+- 1 < 3,调用 c.handlers[1],即 B
+- 执行 part3,调用 c.Next()
+- c.index++,c.index 变为 2
+- 2 < 3,调用 c.handlers[2],即Handler
+- Handler 调用完毕,返回到 B 中的 part4,执行 part4
+- part4 执行完毕,返回到 A 中的 part2,执行 part2
+- part2 执行完毕,结束。
+
+一句话说清楚重点,最终的顺序是`part1 -> part3 -> Handler -> part 4 -> part2`。恰恰满足了我们对中间件的要求,接下来看调用部分的代码,就能全部串起来了。
+
+
+## 代码实现
+
+- 定义`Use`函数,将中间件应用到某个 Group 。
+
+**[day4-group/gee/gee.go](https://github.com/geektutu/7days-golang/tree/master/day5-middleware/gee)**
+
+```go
+// Use is defined to add middleware to the group
+func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
+ group.middlewares = append(group.middlewares, middlewares...)
+}
+
+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...)
+ }
+ }
+ c := newContext(w, req)
+ c.handlers = middlewares
+ engine.router.handle(c)
+}
+```
+
+ServeHTTP 函数也有变化,当我们接收到一个具体请求时,要判断该请求适用于哪些中间件,在这里我们简单通过 URL 的前缀来判断。得到中间件列表后,赋值给 `c.handlers`。
+
+- handle 函数中,将从路由匹配得到的 Handler 添加到 `c.handlers`列表中,执行`c.Next()`。
+
+**[day4-group/gee/router.go](https://github.com/geektutu/7days-golang/tree/master/day5-middleware/gee)**
+
+```go
+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: %s\n", c.Path)
+ })
+ }
+ c.Next()
+}
+```
+
+## 使用 Demo
+
+```go
+func onlyForV2() gee.HandlerFunc {
+ return func(c *gee.Context) {
+ // Start timer
+ t := time.Now()
+ // if a server error occurred
+ c.Fail(500, "Internal Server Error")
+ // 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")
+}
+```
+
+`gee.Logger()`即我们一开始就介绍的中间件,我们将这个中间件和框架代码放在了一起,作为框架默认提供的中间件。在这个例子中,我们将`gee.Logger()`应用在了全局,所有的路由都会应用该中间件。`onlyForV2()`是用来测试功能的,仅在`v2`对应的 Group 中应用了。
+
+接下来使用 curl 测试,可以看到,v2 Group 2个中间件都生效了。
+
+```bash
+$ curl http://localhost:9999/
+>>> log
+2019/08/17 01:37:38 [200] / in 3.14µs
+
+(2) global + group middleware
+$ curl http://localhost:9999/v2/hello/geektutu
+>>> log
+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
+```
\ No newline at end of file
diff --git a/doc/gee-day5/middleware.jpg b/doc/gee-day5/middleware.jpg
new file mode 100644
index 0000000..4933b1e
Binary files /dev/null and b/doc/gee-day5/middleware.jpg differ
diff --git a/doc/gee.md b/doc/gee.md
index 602315a..0a43c62 100644
--- a/doc/gee.md
+++ b/doc/gee.md
@@ -1,7 +1,7 @@
---
title: 7天用Go从零实现Web框架Gee教程
date: 2019-08-11 02:10:10
-description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,从零设计一个Web框架
+description: 7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。
tags:
- Go
categories:
@@ -65,7 +65,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
- [第一天:前置知识(http.Handler接口)](https://geektutu.com/post/gee-day1.html),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day1-http-base)
- [第二天:上下文设计(Context)](https://geektutu.com/post/gee-day2.html),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day2-context)
- [第三天:Tire树路由(Router)](https://geektutu.com/post/gee-day3.html),[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),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day5-middleware)
+- [第四天:分组控制(Group)](https://geektutu.com/post/gee-day4.html),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day4-group)
+- [第五天:中间件(Middleware)]((https://geektutu.com/post/gee-day5.html),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day5-middleware)
- 第六天:HTML模板(Template),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day6-template)
- 第七天:错误恢复(Panic Recover),[Code - Github](https://github.com/geektutu/7days-golang/tree/master/day7-panic-recover)
\ No newline at end of file