免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

跟大佬学Go:Day Four 路由分组(RouterGroup)

跟大佬学Go:Day Four 路由分组(RouterGroup) 今日学习网址https://geektutu.com/post/gee-day4.html分组的意义解决的问题是当涉及到多个接口的时候一个一个注册完整的路径太过繁杂不仅不方便管理而且代码会十分冗余。又因为我们对某一组路由的处理方式是类似的例如以/post开头的路由匿名可访问。以/admin开头的路由需要鉴权。以/api开头的路由是 RESTful 接口可以对接第三方平台需要三方平台鉴权。所以我们可以根据通过相同的前缀来区分并进行分组控制并且支持分组的嵌套例如/post是一个分组/post/a和/post/b可以是该分组下的子分组。作用在/post分组上的中间件(middleware)也都会作用在子分组子分组还可以应用自己特有的中间件。可以共享中间件的就可以干出很多事情了比如在根目录添加记录日志的中间件那么所有路由都可以记录日志。分组嵌套Group对象需要具备哪些属性呢前缀prefix要进行分组嵌套需要知道当前路由的父亲pattern是谁中间件middlewares存储应用在该分组上的中间件访问路由Router的能力之前是通过(*Engine).addRouter来映射所有路由规则以及handler的所以需要给予访问路由的能力。大佬想到了一个简单的方法保存一个指针指向Engine因为所有资源都由Engine统一协调就可以通过Engine间接的访问所有接口Group的定义如下typeRouterGroupstruct{prefixstringmiddlewares[]HandlerFunc// support middlewareparent*RouterGroup// support nestingengine*Engine// all groups share a Engine instance}此时大佬再次展示操作进一步抽象“将Engine作为最顶层的分组也就是说Engine拥有RouterGroup所有的能力。”// Engine implement the interface of ServerHTTPtypeEnginestruct{*RouterGroup router*router groups[]*RouterGroup// store all groups}将和路由有关的函数都将RouterGroup去处理实现// Group is defined to create a new RouterGroup// remember all groups share the same Engine instancefunc(group*RouterGroup)Group(prefixstring)*RouterGroup{engine:group.engine newGroup:RouterGroup{prefix:group.prefixprefix,parent:group,engine:engine,}engine.groupsappend(engine.groups,newGroup)returnnewGroup}func(group*RouterGroup)addRouter(methodstring,compstring,handler HandlerFunc){pattern:group.prefixcomp log.Print(Route %4s - %s,method,pattern)group.engine.router.addRoute(method,pattern,handler)}// GET defines the method to add GET requestfunc(group*RouterGroup)GET(patternstring,handler HandlerFunc){group.addRouter(GET,pattern,handler)}// POST defines the method to add POST requestfunc(group*RouterGroup)POST(patternstring,handler HandlerFunc){group.addRouter(POST,pattern,handler)}其中比较神奇的是addRouter函数中调用group.engine.router.addRoute来实现路由的映射这样既可以添加路由也可以通过分组添加路由main.gofuncmain(){r:gee.New()r.GET(/index,func(c*gee.Context){c.HTML(http.StatusOK,h1Index Page/h1)})v1:r.Group(/v1){v1.GET(/,func(c*gee.Context){c.HTML(http.StatusOK,h1Hello Gee/h1)})v1.GET(/hello,func(c*gee.Context){c.String(http.StatusOK,hello %s, youre at %s\n,c.Query(name),c.Path)})}v2:r.Group(/v2){v2.GET(/hello/:name,func(c*gee.Context){c.String(http.StatusOK,hello %s, youre at %s\n,c.Query(name),c.Path)})v2.POST(/login,func(c*gee.Context){c.JSON(http.StatusOK,gee.H{usename:c.PostForm(usename),password:c.PostForm(password),})})}r.Run(:9999)}执行顺序程序启动时文件调用顺序main.go → gee.go → router.go → trie.go执行过程main.go执行main()调用gee.New()创建框架实例。gee.go创建Engine、初始化根路由分组并调用newRouter()。router.go初始化路由管理器创建roots和handlers两个 map。main.go调用GET()、POST()或Group()注册路由。gee.go拼接路由分组前缀将完整路由交给router.addRoute()。router.go解析路由路径并调用trie.go中的insert()。trie.go将路由规则插入 Trie 前缀树。router.go将路由对应的处理函数保存到handlers。main.go调用r.Run(:9999)启动 HTTP 服务器等待客户端请求。处理 HTTP 请求时文件调用顺序gee.go → context.go → router.go → trie.go → router.go → 业务 Handler → context.go执行过程gee.goEngine.ServeHTTP()接收 HTTP 请求。context.go调用newContext()创建当前请求的Context保存请求路径、请求方法等信息。router.go调用router.handle()通过getRoute()查找匹配的路由。trie.go调用search()利用 Trie 前缀树匹配请求路径。router.go获取匹配结果提取动态路由参数并找到对应的 Handler。业务 Handler执行main.go中注册的业务处理函数。context.go通过c.String()、c.JSON()等方法生成 HTTP 响应。客户端接收服务器返回的响应。测试curl http://localhost:9999/v1/hello?nameyuexi curl http://localhost:9999/v2/hello/yuexi结果hello yuexi, youre at /v1/hello hello , youre at /v2/hello/yuexi
返回列表