mirror of
https://github.com/geektutu/7days-golang.git
synced 2024-04-21 12:32:11 +00:00
add day4-day5 doc & fix some comments
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ type (
|
||||
Engine struct {
|
||||
*RouterGroup
|
||||
router *router
|
||||
groups []*RouterGroup // store all group
|
||||
groups []*RouterGroup // store all groups
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
+16
-5
@@ -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
|
||||
<h1>Index Page</h1>
|
||||
|
||||
(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
|
||||
<h1>Hello Gee</h1>
|
||||
|
||||
(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, "<h1>Index Page</h1>")
|
||||
})
|
||||
v1 := r.Group("/v1")
|
||||
{
|
||||
v1.GET("/", func(c *gee.Context) {
|
||||
|
||||
@@ -20,7 +20,7 @@ type (
|
||||
Engine struct {
|
||||
*RouterGroup
|
||||
router *router
|
||||
groups []*RouterGroup // store all group
|
||||
groups []*RouterGroup // store all groups
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
+7
-2
@@ -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`等。
|
||||
|
||||
+164
@@ -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, "<h1>Hello Gee</h1>")
|
||||
})
|
||||
```
|
||||
|
||||
那么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, "<h1>Index Page</h1>")
|
||||
})
|
||||
v1 := r.Group("/v1")
|
||||
{
|
||||
v1.GET("/", func(c *gee.Context) {
|
||||
c.HTML(http.StatusOK, "<h1>Hello Gee</h1>")
|
||||
})
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
+225
@@ -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, "<h1>Hello Gee</h1>")
|
||||
})
|
||||
|
||||
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
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
+3
-3
@@ -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)
|
||||
Reference in New Issue
Block a user