在上一篇的基础之上继续实现前缀路由的功能。
首先说明一下这边说的前缀路由的概念。在上一篇的Web服务的基础上,我们对于路由可以有两种处理方式:
-
一种是所有的路由都挂载到根路径 ‘/’ 上边,举例说明,如果有一个登录和登出功能,我们可以这么来写
复制代码1
2
3router.GET("/user/login", loginHandler) router.GET("/user/logout", logoutHandler)
当然这也是最为直观和容易理解的方式。
-
另一种是将有共通路径前缀的路由集合在一块,例如按照Gin的写法:
复制代码1
2
3
4
5
6user := route.Group("/user") { user.GET("/login", loginHandler) user.GET("/logout", logoutHandler) }
当然各有各的好处了, 这种方式的方便之处在于,如果要对 “/user” 前缀的路由统一做处理(比如增加鉴权功能),就只需要在最上层加上处理逻辑,无需对每一个路由重复添加。尤其是在Gin里边,搭配后边会介绍的中间件一块使用,会显得更为方便。
接下来就介绍Gin里边如何实现的这一功能。同时我们慢慢的构建一个框架应该有的样子。
使用框架的目的是什么,我的理解是隐藏重复和底层的细节,使其作为一个可重复的抽象,让开发更为快速和便捷。前两篇都是直接用一个文件搭建了Web服务,现在我们开始真正的做一个抽象。
我们先抽象出来一个引擎 Engine,那么这个Engine里边应该有什么东西,很直观的,我们需要上一篇讲到的路由功能,以及这一篇讲到的路由前缀。定义如下:
1
2
3
4
5Engine struct { *RouterGroup router *httprouter.Router }
假设我们已经完成了这个 Engine, 我们需要一个启动函数
1
2
3
4func (engine *Engine) Run(addr string) { http.ListenAndServe(addr, engine) }
看起来很简单,就是把上一篇中最后的启动函数 http.ListenAndServe 放到了Run里边而已。 区别只是把 engine 当作了第二个参数传入。而第二个参数其实只是 interface, 里边有一个函数
1
2ServeHTTP(ResponseWriter, *Request)
所以只要 engine 也实现了这个函数,就能作为第二个参数了,那我们就实现一个。
1
2
3
4
5// ServeHTTP makes the router implement the http.Handler interface. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { engine.router.ServeHTTP(w, req) }
也是很简单,只是调用了 engine里边 httprouter.Router 的 ServeHTTP。
我们已经有了启动函数,接下去看看初始化的过程。先看engine中RouterGroup的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// HandlerFunc . HandlerFunc func(*Context) // Context . Context struct { Req *http.Request Writer http.ResponseWriter Params httprouter.Params handler HandlerFunc engine *Engine } // RouterGroup . RouterGroup struct { Handler HandlerFunc prefix string parent *RouterGroup engine *Engine }
这边定义了一个Context,这个是gin中最重要的数据结构之一。它把request的Req、Write以及Params都包在了里边。所以初始化的过程也就明确了
1
2
3
4
5
6
7func New() *Engine { engine := &Engine{} engine.RouterGroup = &RouterGroup{nil, "", nil, engine} engine.router = httprouter.New() return engine }
接下去定义group的时候,创建一个新的 RouterGroup
1
2
3
4
5
6
7
8
9
10
11// Group . func (group *RouterGroup) Group(component string) *RouterGroup { prefix := path.Join(group.prefix, component) return &RouterGroup{ Handler: nil, parent: group, prefix: prefix, engine: group.engine, } }
然后调用
1
2user.GET("/login", loginHandler)
的时候需要把对应的loginHandler加入到router的路由配置中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// GET is a shortcut for router.Handle("GET", path, handle) func (group *RouterGroup) GET(path string, handler HandlerFunc) { group.Handle("GET", path, handler) } // Handle . func (group *RouterGroup) Handle(method, p string, handler HandlerFunc) { p = path.Join(group.prefix, p) group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { group.createContext(w, req, params, handler).Next() }) } func (c *Context) Next() { c.handler(c) }
从上边的代码可以看出, user.GET 实际上就是先把routerGroup的前缀路径组合起来,然后将对应的路径和handler加入到httprouter里边。
同样的,类似GET方法的处理, 我们能定义其他的比如POST、DELETE的方法实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// POST is a shortcut for router.Handle("POST", path, handle) func (group *RouterGroup) POST(path string, handler HandlerFunc) { group.Handle("POST", path, handler) } // GET is a shortcut for router.Handle("GET", path, handle) func (group *RouterGroup) GET(path string, handler HandlerFunc) { group.Handle("GET", path, handler) } // DELETE is a shortcut for router.Handle("DELETE", path, handle) func (group *RouterGroup) DELETE(path string, handler HandlerFunc) { group.Handle("DELETE", path, handler) } // PATCH is a shortcut for router.Handle("PATCH", path, handle) func (group *RouterGroup) PATCH(path string, handler HandlerFunc) { group.Handle("PATCH", path, handler) } // PUT is a shortcut for router.Handle("PUT", path, handle) func (group *RouterGroup) PUT(path string, handler HandlerFunc) { group.Handle("PUT", path, handler) }
这样一个最简单的框架其实就已经成形了, 那么怎么使用这个框架呢, 我们把上一篇的main.go文件进行一下修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package main import ( "github.com/harleylau/myGin/v0.1/gin" ) func v1IndexLoginfunc(c *gin.Context) { c.String(200, "login") } func v1IndexSubmitfunc(c *gin.Context) { c.String(200, "submit") } func main() { r := gin.New() // Simple group: v1 v1 := r.Group("/v1") { v1.GET("/login", v1IndexLoginfunc) v1.GET("/submit", v1IndexSubmitfunc) } // Listen and server on 0.0.0.0:8080 r.Run(":8080") }
具体的代码实现可以参见github: https://github.com/harleylau/myGin/tree/master/v0.1
下一篇介绍Web框架中的中间件技术。
最后
以上就是忧伤大侠最近收集整理的关于跟Gin一块搭建自己的web框架(三)的全部内容,更多相关跟Gin一块搭建自己内容请搜索靠谱客的其他文章。
发表评论 取消回复