blog
blog copied to clipboard
Go语言动手写Web框架 - Gee第四天 分组控制Group | 极客兔兔
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)的意义,以及嵌套分组路由的实现。
很好的入门文章!感谢博主!
Day4中的Engine定义有一点疑问请教:
Engine struct {
*RouterGroup
router *router
groups []*RouterGroup // store all groups
}
这个RouterGroup是Engine的一个属性吗?
Go接触不久,还请指教。
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.
@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 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.
@wzc314 这是 go 中的嵌套类型,类似 Java/Python 等语言的继承。这样 Engine 就可以拥有 RouterGroup 的属性了。
感谢博主! 我想问一下,RouteGroup结构体中的parent属性是否可以删去, 因为在使用的的过程中,好像并没有需要 获取一个group的parent的地方。
@201732110125 parent 之前设计是用来拼接 prefix 的,每个 group 只记录自己的部分,最后通过 parent 层层回溯拼接。不过后来改用 group.prefix + prefix
的方式 group 初始化时已经拼接了完整的 prefix,所以不需要 parent
了,可以删除。
我认为这里的 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 Go语言的嵌套在其他语言中类似于继承,子类必然是比父类有更多的成员变量和方法。RouterGroup 仅仅是负责分组路由,Engine 除了分组路由外,还有很多其他的功能。RouterGroup 继承 Engine 的 Run()
,ServeHTTP
等方法是没有意义的。
@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
等接口功能
groups暂时没有作用,是否是后面使用?
看文章学一半,看评论再学一半/xk
Engine 和 RouterGroup 是否属于循环嵌套了呢
如果只是用前缀当分组的话会不会适用场景不高?我试了下如果是这个场景
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
}
刚初学,问题有点多,见谅。
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()是什么用法。。。望指点
- -!明白了,忽略
@sunanzhi 这个就看如何去设计了,前缀区分一般是比较好的方式。比如
对一个博客系统来说,/auth
,/user
下的走授权中间件,/post/<id>.html
,就不走授权,走统计中间件。/api/
开头的,是对外提供的公共接口,诸如此类。是比较符合 URL 设计的习惯的。
特别是 Restful API,以资源为中心的 URL 设计,通过前缀做不同业务的区分更为明显,不同前缀代表不同类型的资源。
请教一下:
1、能不能在路由的前缀树节点中直接记录分组以及中间件函数呢?node
中增加一个是否分组的标记,以及中间件列表。省去Engine
和RouterGroup
相互引用
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
}
@geektutu @201732110125 parent 之前设计是用来拼接 prefix 的,每个 group 只记录自己的部分,最后通过 parent 层层回溯拼接。不过后来改用
group.prefix + prefix
的方式 group 初始化时已经拼接了完整的 prefix,所以不需要parent
了,可以删除。
如果去掉parent,在一个group中再创建一个group怎么办?
@How-invin 看文章学一半,看评论再学一半/xk
没错没错
@fishjar 你的想法是没问题的,当前的这种实现,前缀树解耦得比较好,功能比较单一,容易替换为其他的前缀树实现。Gin 这部分一开始也是利用了第三方实现,后来替换为自己的。
@echomuof 再创建子 group 的话,用当前 group 的 prefix 拼接应该就 OK 。
@xlban163 哈哈,也感谢你的评论。
@geektutu @sunanzhi 这个就看如何去设计了,前缀区分一般是比较好的方式。比如
对一个博客系统来说,
/auth
,/user
下的走授权中间件,/post/<id>.html
,就不走授权,走统计中间件。/api/
开头的,是对外提供的公共接口,诸如此类。是比较符合 URL 设计的习惯的。特别是 Restful API,以资源为中心的 URL 设计,通过前缀做不同业务的区分更为明显,不同前缀代表不同类型的资源。 同学, 可以这样理解, /auth 或 /user 是和使用者操作相关,服务器需要判断该使用者是否授权。 /post/
.html 是网页自身需要统计一些信息触发的,不需要授权。
这里RouterGroup和Engine是循环引用吗? 在写的时候犯了个错误
把GET函数中的group.addRoute("GET", pattern, handler) 写成了group.engine.addRoute("GET", pattern, handler)
导致生成的prefix是空字符串,请问这是为什么呢?理论上group指针的prefix应该是有值的,小白求教
@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 == ""
👍解答好详细,受教了。
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 .
有个问题,请问engine的结构体中为什么要包含RouterGroup,是做什么用的?
@huchiZhang 有个问题,请问engine的结构体中为什么要包含RouterGroup,是做什么用的?
group中肯定要包含engine来利用之前engine中已实现的router来完成路由分组,至于为什么engine要包含routergroup?我觉得也可以不包含 但是你每次用的时候 得先创建engine然后再创建group再开始注册handlerfunc这样做很别扭 engine作为web框架的引擎来说统一由它管理则是最好的 包括后面的中间件管理也都由engine来管理 面对用户则是干净整洁的接口 不知道说的对不对
博主,好像:Engine 的 groups 没有用上?