blog icon indicating copy to clipboard operation
blog copied to clipboard

Go语言动手写Web框架 - Gee第四天 分组控制Group | 极客兔兔

Open geektutu opened this issue 5 years ago • 62 comments

https://geektutu.com/post/gee-day4.html

7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,以 Gin 为原型从零设计一个Web框架。本文介绍了分组控制(Group Control)的意义,以及嵌套分组路由的实现。

geektutu avatar Sep 01 '19 13:09 geektutu

很好的入门文章!感谢博主!

Day4中的Engine定义有一点疑问请教:

Engine struct {
	*RouterGroup
	router *router
	groups []*RouterGroup // store all groups
}

这个RouterGroup是Engine的一个属性吗?

Go接触不久,还请指教。

wzc314 avatar Feb 22 '20 01:02 wzc314

Wonderful article series, Thanks for sharing the knowledge. I cannot read the text but I have been using google translate to follow along & code. This type of article helps understand the concept without all the noise of a big project or optimisation. Please can you write an article and implementing a B+ tree for key value store in Go? a bit like the one in BoltDB.

evanxg852000 avatar Feb 22 '20 09:02 evanxg852000

@wzc314 很好的入门文章!感谢博主!

Day4中的Engine定义有一点疑问请教:

Engine struct {
	*RouterGroup
	router *router
	groups []*RouterGroup // store all groups
}

这个RouterGroup是Engine的一个属性吗?

Go接触不久,还请指教。

This is called embeded type in Go Read the section Embeding of this guide https://github.com/hoanhan101/ultimate-go

evanxg852000 avatar Feb 22 '20 09:02 evanxg852000

@evanxg852000 I'm very glad to see your comment. I'm sorry and I'm busy writing the 3rd series, a ORM framework geeorm now. There is a golang version for bolt, named bbolt. I would like to imitate bbolt to implement geebolt, maybe in several weeks. It's a wonderful choice as the 4th, thanks for your suggestion.

geektutu avatar Feb 22 '20 10:02 geektutu

@wzc314 这是 go 中的嵌套类型,类似 Java/Python 等语言的继承。这样 Engine 就可以拥有 RouterGroup 的属性了。

geektutu avatar Feb 22 '20 10:02 geektutu

感谢博主! 我想问一下,RouteGroup结构体中的parent属性是否可以删去, 因为在使用的的过程中,好像并没有需要 获取一个group的parent的地方。

201732110125 avatar Mar 06 '20 08:03 201732110125

@201732110125 parent 之前设计是用来拼接 prefix 的,每个 group 只记录自己的部分,最后通过 parent 层层回溯拼接。不过后来改用 group.prefix + prefix 的方式 group 初始化时已经拼接了完整的 prefix,所以不需要 parent 了,可以删除。

geektutu avatar Mar 06 '20 09:03 geektutu

我认为这里的 RouterGroup 应该嵌套 Engine 而不是反过来,gee.New() 的时候初始化一个 Engine,然后返回一个持有自己的 RouterGroup 就 OK。当然差别也不是特别大,不过从面向对象的角度舒服一点。

func New() *RouterGroup {
	engine := &Engine{router: newRouter()}
	return &RouterGroup{
		Engine: engine,
	}
}

func (group *RouterGroup) Group(prefix string) *RouterGroup {
	newGroup := &RouterGroup{
		prefix: group.prefix + prefix,
		Engine: group.Engine,
	}
	group.Engine.groups = append(group.Engine.groups , newGroup)
	return newGroup
}

xenv avatar Apr 19 '20 11:04 xenv

@xenv Go语言的嵌套在其他语言中类似于继承,子类必然是比父类有更多的成员变量和方法。RouterGroup 仅仅是负责分组路由,Engine 除了分组路由外,还有很多其他的功能。RouterGroup 继承 Engine 的 Run()ServeHTTP 等方法是没有意义的。

geektutu avatar Apr 19 '20 14:04 geektutu

@geektutu @xenv Go语言的嵌套在其他语言中类似于继承,子类必然是比父类有更多的成员变量和方法。RouterGroup 仅仅是负责分组路由,Engine 除了分组路由外,还有很多其他的功能。RouterGroup 继承 Engine 的 Run()ServeHTTP 等方法是没有意义的。

type RouterGroup struct {
	prefix      string // 支持叠加
	middlewares []HandlerFunc
	router      *router
}

type Engine struct {
	*RouterGroup
}

func New() *Engine {
	engine := &Engine{}
	engine.RouterGroup = &RouterGroup{router: newRouter()}
	return engine
}

func (group *RouterGroup) Group(prefix string) *RouterGroup {
	newGroup := &RouterGroup{
		prefix: group.prefix + prefix,
		router: group.router,
	}
	return newGroup
}

感觉组合关系这样子好像比较容易理解, RouterGroup继承了router的路由功能, Engine继承了RouterGroup的分组功能, 同时还有其他的Run, ServeHTTP等接口功能

SourceLink avatar Apr 20 '20 06:04 SourceLink

groups暂时没有作用,是否是后面使用?

dont-see-big-shark avatar Apr 22 '20 09:04 dont-see-big-shark

看文章学一半,看评论再学一半/xk

How-invin avatar Jul 29 '20 08:07 How-invin

Engine 和 RouterGroup 是否属于循环嵌套了呢

banzhihang avatar Aug 21 '20 03:08 banzhihang

如果只是用前缀当分组的话会不会适用场景不高?我试了下如果是这个场景

v1.Group("v1")
v1.Use(gee.Logger())
v1.GET("/run", func(c *gee.Context) {
		c.Json(http.StatusOK,  student{})
	})
r.GET("/v1/say", func(c *gee.Context) {
	c.Json(http.StatusOK, student{})
})

那么 v1/say也会执行中间件的代码 所以将group设置的时候分开一下,用key去区分会不会适用场景多点?可以明确同prefix的情况下不同后缀也可以执行不同的middleware

// 分组配置
type GroupConfig struct {
	Prefix string // 前缀
	Key    string // 分组key
}

刚初学,问题有点多,见谅。

sunanzhi avatar Aug 26 '20 10:08 sunanzhi

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 groups
}

) go小白,这个type()是什么用法。。。望指点

Licccccc avatar Sep 23 '20 03:09 Licccccc

  • -!明白了,忽略

Licccccc avatar Sep 29 '20 08:09 Licccccc

@sunanzhi 这个就看如何去设计了,前缀区分一般是比较好的方式。比如

对一个博客系统来说,/auth/user 下的走授权中间件,/post/<id>.html,就不走授权,走统计中间件。/api/ 开头的,是对外提供的公共接口,诸如此类。是比较符合 URL 设计的习惯的。

特别是 Restful API,以资源为中心的 URL 设计,通过前缀做不同业务的区分更为明显,不同前缀代表不同类型的资源。

geektutu avatar Sep 29 '20 08:09 geektutu

请教一下:

1、能不能在路由的前缀树节点中直接记录分组以及中间件函数呢?node中增加一个是否分组的标记,以及中间件列表。省去EngineRouterGroup相互引用

2、另外,我觉得路由的handler是否也可以直接挂载到路由节点中呢?

大概这样:

// router.go
type router struct {
	root    *node
}
// trie.go
type node struct {
	pattern     string
	part        string
	children    []*node
	isWild      bool
	isGroup     bool
	middlewares []HandlerFunc
	handlers    map[string]HandlerFunc
}

fishjar avatar Nov 11 '20 08:11 fishjar

@geektutu @201732110125 parent 之前设计是用来拼接 prefix 的,每个 group 只记录自己的部分,最后通过 parent 层层回溯拼接。不过后来改用 group.prefix + prefix 的方式 group 初始化时已经拼接了完整的 prefix,所以不需要 parent 了,可以删除。

如果去掉parent,在一个group中再创建一个group怎么办?

TuringBecky avatar Nov 17 '20 15:11 TuringBecky

@How-invin 看文章学一半,看评论再学一半/xk

没错没错

xlban163 avatar Nov 18 '20 03:11 xlban163

@fishjar 你的想法是没问题的,当前的这种实现,前缀树解耦得比较好,功能比较单一,容易替换为其他的前缀树实现。Gin 这部分一开始也是利用了第三方实现,后来替换为自己的。

geektutu avatar Nov 22 '20 14:11 geektutu

@echomuof 再创建子 group 的话,用当前 group 的 prefix 拼接应该就 OK 。

geektutu avatar Nov 22 '20 14:11 geektutu

@xlban163 哈哈,也感谢你的评论。

geektutu avatar Nov 22 '20 14:11 geektutu

@geektutu @sunanzhi 这个就看如何去设计了,前缀区分一般是比较好的方式。比如

对一个博客系统来说,/auth/user 下的走授权中间件,/post/<id>.html,就不走授权,走统计中间件。/api/ 开头的,是对外提供的公共接口,诸如此类。是比较符合 URL 设计的习惯的。

特别是 Restful API,以资源为中心的 URL 设计,通过前缀做不同业务的区分更为明显,不同前缀代表不同类型的资源。 同学, 可以这样理解, /auth 或 /user 是和使用者操作相关,服务器需要判断该使用者是否授权。 /post/.html 是网页自身需要统计一些信息触发的,不需要授权。

feixintianxia avatar Dec 28 '20 03:12 feixintianxia

这里RouterGroup和Engine是循环引用吗? 在写的时候犯了个错误

把GET函数中的group.addRoute("GET", pattern, handler) 写成了group.engine.addRoute("GET", pattern, handler)

导致生成的prefix是空字符串,请问这是为什么呢?理论上group指针的prefix应该是有值的,小白求教

YellowPure avatar Feb 04 '21 08:02 YellowPure

@YellowPure 这里RouterGroup和Engine是循环引用吗? 在写的时候犯了个错误

把GET函数中的group.addRoute("GET", pattern, handler) 写成了group.engine.addRoute("GET", pattern, handler)

导致生成的prefix是空字符串,请问这是为什么呢?理论上group指针的prefix应该是有值的,小白求教

调用group.engine.addRoute时,使用的是RouterGroup里面的engine结构从RouterGroup继承的addRoute方法,也就是说addRoute的作用对象是engine里面的RouteGroup指针指向的group变量。 观察函数gee.Group可以发现,当创建一个新的RouterGroup变量时,该变量内的engine结构实际上等于上一个group内的engine,而上一个engine内的RouterGroup指针指向的的group变量的prefix并没有加上当前Group函数传入的prefix,所以当你调用group.engine.addRoute时,得到的prefix实际上是调用gee.Group函数得到新的group之前的prefix。在给出的demo里,自然v1.engine.RouterGroup.prefix == ""

LAShZ avatar Feb 06 '21 14:02 LAShZ

👍解答好详细,受教了。

Lavch [email protected] 于2021年2月6日周六 下午10:55写道:

@YellowPure https://github.com/YellowPure 这里RouterGroup和Engine是循环引用吗? 在写的时候犯了个错误

把GET函数中的group.addRoute("GET", pattern, handler) 写成了group.engine.addRoute("GET", pattern, handler)

导致生成的prefix是空字符串,请问这是为什么呢?理论上group指针的prefix应该是有值的,小白求教

调用group.engine.addRoute 时,使用的是RouterGroup里面的engine结构从RouterGroup继承的addRoute方法,也就是说addRoute的作用对象是engine里面的RouteGroup指针指向的group变量。 观察函数gee.Group 可以发现,当创建一个新的RouterGroup变量时,该变量内的engine结构实际上等于上一个group内的engine,而上一个engine内的RouterGroup指针指向的的group变量的prefix并没有加上当前Group函数传入的prefix,所以当你调用 group.engine.addRoute时,得到的prefix实际上是调用gee.Group函数得到新的group之前的prefix。在给出的demo里,自然v1.engine.RouterGroup.prefix == ""

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/geektutu/blog/issues/44#issuecomment-774489157, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABN2XRU2C3UJJKBJ5LSFGC3S5VJ5NANCNFSM4ISW2NUQ .

YellowPure avatar Feb 18 '21 07:02 YellowPure

有个问题,请问engine的结构体中为什么要包含RouterGroup,是做什么用的?

huchiZhang avatar Apr 06 '21 03:04 huchiZhang

@huchiZhang 有个问题,请问engine的结构体中为什么要包含RouterGroup,是做什么用的?

group中肯定要包含engine来利用之前engine中已实现的router来完成路由分组,至于为什么engine要包含routergroup?我觉得也可以不包含 但是你每次用的时候 得先创建engine然后再创建group再开始注册handlerfunc这样做很别扭 engine作为web框架的引擎来说统一由它管理则是最好的 包括后面的中间件管理也都由engine来管理 面对用户则是干净整洁的接口 不知道说的对不对

walkmiao avatar Apr 12 '21 12:04 walkmiao

博主,好像:Engine 的 groups 没有用上?

Kimgtb avatar May 06 '21 05:05 Kimgtb