Golang中HTTP路由设计的使用与实现
来源:脚本之家
2023-05-12 11:16:58
0浏览
收藏
“纵有疾风来,人生不言弃”,这句话送给正在学习Golang的朋友们,也希望在阅读本文《Golang中HTTP路由设计的使用与实现》后,能够真的帮助到大家。我也会在后续的文章中,陆续更新Golang相关的技术文章,有好的建议欢迎大家在评论留言,非常感谢!
Golang之HTTP路由设计
为什么要设计路由规则,路由规则是HTTP的请求按照一定的规则 ,匹配查找到对应的控制器并传递执行的逻辑!
自己编写路由的话需要注意一下一共有几种路由!
- 一种是支持原生的restful四种类型的访问方法!
Get
,Post
,Delete
,Put
- 需要支持自定义的路径,也就是静态路由
- 批量通用前缀,也就是下面我们将讲到的
group
- 动态路由匹配!
也就是像这样我们在route.go
去注册
func registerRouter(core *framework.Core) { print(111) // 设置控制器 core.Get("/foo", FooController) core.Get("/user/login", UserLoginController) subjectApi := core.Group("/subject") { // restful路由,根据请求类型区分了开,:id为动态路由 subjectApi.Get("/list/all", SubjectListController) subjectApi.Post("/add", SubjectListController) subjectApi.Delete("/:id", SubjectListController) subjectApi.Put("/:id", SubjectListController) subjectApi.Get("/:id", SubjectListController) } }
动手编写自己的路由
在上一节中我们编写了自己的请求处理器,对应在里面加入我们的路由规则就好了!
framework/core.go
package framework import ( "net/http" "strings" ) const ( GET = "GET" PUT = "PUT" DELETE = "DELETE" POST = "POST" ) //map[string]map[string]ControllerHandler 前面存请求类型后面是路径对应执行方法 type Core struct { router map[string]map[string]ControllerHandler } func (c Core) ServeHTTP(writer http.ResponseWriter, request *http.Request) { ctx:= NewContext(request, writer) router:=c.FindRouteByRequest(request) if router==nil{ ctx.Json(404,"router not found ") return } if err:=router(ctx);err!=nil{ ctx.Json(500,"server Interval") return } //http.DefaultServeMux.ServeHTTP(writer, request) } func NewCore() *Core { getRouter := map[string]ControllerHandler{} postRouter := map[string]ControllerHandler{} putRouter := map[string]ControllerHandler{} deleteRouter := map[string]ControllerHandler{} core := &Core{ router: make(map[string]map[string]ControllerHandler, 0), } // 初始化好四种类型的路由map core.router[GET] = getRouter core.router[POST] = postRouter core.router[PUT] = putRouter core.router[DELETE] = deleteRouter return core } // 注册Get方法 func (c *Core) Get(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) c.router[GET][url] = handler } // 注册Post方法 func (c *Core) Post(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) // 大小写不敏感 c.router[POST][url] = handler } // 注册Put方法 func (c *Core) Put(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) c.router[PUT][url] = handler } // 注册Delete方法 func (c *Core) Delete(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) c.router[DELETE][url] = handler } // 寻找http+静态路由 func (c *Core) FindRouteByRequest(request *http.Request) ControllerHandler { uri := request.URL.Path //请求处理器映射地址 method := request.Method // 请求类型 upperMethod := strings.ToUpper(method) upperURI := strings.ToUpper(uri) // 找到类型下的具体地址的映射地址的方法,这里还没有实现动态什么的就固定有1个路径key,但是先别急,后面我们再来动手改造 if data, ok := c.router[upperMethod]; ok { if handler, ok1 := data[upperURI]; ok1 { return handler } } return nil }
framework/group.go
给我们的注册路由,加上分组,用group包装,这样对应我们在使用group时就会对应到不同的请求类型的方法了!并且在这一层给所有的注册地址统一加上group前缀地址!
package framework //IGroup 代表前缀分组 type IGroup interface { Get(string, ControllerHandler) Post(string, ControllerHandler) Delete(string, ControllerHandler) Put(string, ControllerHandler) } // type Group struct { core *Core // perfix string // 自身前缀 } func (g Group) Get(s string, handler ControllerHandler) { url := g.perfix + s g.core.Get(url, handler) } func (g Group) Post(s string, handler ControllerHandler) { url := g.perfix + s g.core.Post(url, handler) } func (g Group) Delete(s string, handler ControllerHandler) { url := g.perfix + s g.core.Delete(url, handler) } func (g Group) Put(s string, handler ControllerHandler) { url := g.perfix + s g.core.Put(url, handler) } func NewGroup(core *Core, perfix string) *Group { return &Group{core: core, perfix: perfix} } func (c *Core)Group(prefix string)IGroup{ return NewGroup(c,prefix) }
如何实现动态路由
首先先定义好我们的动态路由数据结构
// 实现动态路由匹配树 type Tree struct { root *node // 根结点 } // 代表节点 type node struct { isLast bool // 代表这个节点是否可以成为最终的路由规则。 该节点是否能成为一 segment string // url 中的字符串,代表这个节点表示的路由中某个段的字符串 handler ControllerHandler // 代表这个节点中包含的控制器,用于最终加载调用 childes []*node // 代表这个节点下的子节点 }
我们要做的就是在每次注册的时候去将对应的路径的东西将之前的map[string]map[string]ControllerHandler
替换为新改造的这个Tree!
从node的结构来看我们应该判断我们的segment
去添加我们的childes的node在最后的节点的时候赋值一下处理方法
//matchNode 方法的参数是一个 URI,返回值是指向 node 的指针,它的实现思路是使用函数递归 // 判断是否动态路由 func isWildSegment(segment string) bool { return strings.HasPrefix(segment, ":") }
下面是我们需要的一些功能函数,递归匹配路由和找到下一层的子节点
//过滤下一层满足 segment 规则的子节点 func (n *node) filterChildNodes(segment string) []*node { if len(n.childes) == 0 { return nil } // 如果是动态路由则子节点直接满足条件 if isWildSegment(segment) { return n.childes } // 不是的话就从子节点里面找2 nodes := make([]*node, 0, len(n.childes)) for _, node := range n.childes { // 判断所有子节点里面是否有动态路由或者唯一匹配的路由 if isWildSegment(node.segment) || node.segment == segment { nodes = append(nodes, node) } } return nodes } // 匹配路由 func (n *node) matchNode(url string) *node { // 正序拆分路由第一个/ segments := strings.SplitN(url, "/", 2) segment := segments[0] // 第一个路由节点 //判断如果不是动态路由,那么都统一大写 if !isWildSegment(segment) { segment = strings.ToUpper(segment) } // 找到下一层路由节点 nodes := n.filterChildNodes(segment) // 错误返回 if nodes == nil || len(nodes)
下面是增加路由,以及提供给外部用的,找到对应执行逻辑的控制器方法!
// 增加路由 func (tree *Tree) AddRoute(url string, handler ControllerHandler) error { n := tree.root // 确认路由是否已存在 if n.matchNode(url) != nil { return errors.New(fmt.Sprintf("add router %v error", url)) } segments := strings.Split(url, "/") // 对每个segment for index, segment := range segments { // 不是动态路由的静态节点 需要转变大写 if !isWildSegment(segment) { segment = strings.ToUpper(segment) } isLast := index == len(segments)-1 // 判断是否为最后一个节点 var objNode *node childNodes := n.filterChildNodes(segment) if len(childNodes) > 0 { // 如果有segment相同的子节点,则选择这个子节点 for _, node := range childNodes { if node.segment == segment { objNode = node break } } } // 如果没有找到相同的子节点,那么就自己构造一个添加进tree里面 if objNode == nil { objNode = &node{ isLast: isLast, segment: segment, handler: nil, childes: make([]*node, 0), } if isLast { objNode.handler = handler } n.childes = append(n.childes, objNode) } n = objNode } return nil } // 寻找对应的映射控制器处理方法 func (tree *Tree) FindHandler(url string) ControllerHandler { // 直接复用 matchNode := tree.root.matchNode(url) if matchNode == nil { return nil } return matchNode.handler }
改造一下core.go
将实现了动态路由的Tree替换进来
package framework import ( "log" "net/http" "strings" ) const ( GET = "GET" PUT = "PUT" DELETE = "DELETE" POST = "POST" ) type Core struct { router map[string]*Tree } func (c Core) ServeHTTP(writer http.ResponseWriter, request *http.Request) { ctx := NewContext(request, writer) router := c.FindRouteByRequest(request) if router == nil { ctx.Json(404, "router not found ") return } if err := router(ctx); err != nil { ctx.Json(500, "server Interval") return } //http.DefaultServeMux.ServeHTTP(writer, request) } func NewCore() *Core { getRouter := NewTree() postRouter := NewTree() putRouter := NewTree() deleteRouter := NewTree() core := &Core{ router: make(map[string]*Tree, 0), } core.router[GET] = getRouter core.router[POST] = postRouter core.router[PUT] = putRouter core.router[DELETE] = deleteRouter return core } // 注册Get方法 func (c *Core) Get(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) if err := c.router[GET].AddRoute(url, handler); err != nil { log.Fatal("add router error:", err) } } // 注册Post方法 func (c *Core) Post(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) // 大小写不敏感 if err := c.router[POST].AddRoute(url, handler); err != nil { log.Fatal("add router error:", err) } } func (c *Core) Put(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) if err := c.router[PUT].AddRoute(url, handler); err != nil { log.Fatal("add router error:", err) } } func (c *Core) Delete(pattern string, handler ControllerHandler) { url := strings.ToUpper(pattern) if err := c.router[DELETE].AddRoute(url, handler); err != nil { log.Fatal("add router error:", err) } } // 寻找http+静态路由 func (c *Core) FindRouteByRequest(request *http.Request) ControllerHandler { uri := request.URL.Path method := request.Method upperMethod := strings.ToUpper(method) // upperURI := strings.ToUpper(uri) 内部路由会去判断非动态会转大写 if data, ok := c.router[upperMethod]; ok { return data.FindHandler(uri) } return nil }
验证
编写两个Controller
func UserLoginController(ctx *framework.Context) error { ctx.Json(200, "ok,UserLoginController") return nil } func SubjectListController(ctx *framework.Context) error { ctx.Json(200, "ok,SubjectListController") return nil }
启动运行
理论要掌握,实操不能落!以上关于《Golang中HTTP路由设计的使用与实现》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
版本声明
本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除

- 上一篇
- Golang HTTP服务超时控制实现原理分析

- 下一篇
- Golang中间件设计示例详解
查看更多
最新文章
-
- Golang · Go教程 | 2分钟前 |
- Golang高效读取大文件方法解析
- 422浏览 收藏
-
- Golang · Go教程 | 26分钟前 |
- Golang插件系统测试:plugin.Open隔离解析
- 373浏览 收藏
-
- Golang · Go教程 | 33分钟前 |
- Golangnet/http教程:搭建服务器与客户端方法
- 217浏览 收藏
-
- Golang · Go教程 | 36分钟前 |
- GolangCookie与Session管理全解析
- 225浏览 收藏
-
- Golang · Go教程 | 36分钟前 |
- Golang原子替换:rename实现与事务解析
- 161浏览 收藏
-
- Golang · Go教程 | 42分钟前 |
- Gin框架构建RESTAPI入门教程
- 455浏览 收藏
-
- Golang · Go教程 | 44分钟前 |
- Golang日志优化:异步缓冲提升效率
- 387浏览 收藏
-
- Golang · Go教程 | 45分钟前 |
- Golang覆盖率统计方法与工具使用技巧
- 119浏览 收藏
-
- Golang · Go教程 | 46分钟前 |
- Golangcontext.WithValue线程安全使用全解析
- 226浏览 收藏
-
- Golang · Go教程 | 48分钟前 |
- Golangselect阻塞问题与channel非阻塞技巧
- 287浏览 收藏
-
- Golang · Go教程 | 54分钟前 |
- Golang接口隐式实现,体现鸭子类型思想
- 341浏览 收藏
-
- Golang · Go教程 | 54分钟前 |
- Golang时间处理性能优化技巧分享
- 373浏览 收藏
查看更多
课程推荐
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
查看更多
AI推荐
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 95次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 89次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 105次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 98次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 97次使用
查看更多
相关文章
-
- HTTP 的 response 中的响应体和头部是分开发送的吗?
- 2023-01-28 387浏览
-
- B站等视频网站的弹幕用的是 websocket 还是轮询?
- 2023-02-16 447浏览
-
- Golang实现HTTP编程请求和响应
- 2022-12-28 101浏览
-
- golangNewRequest/gorequest实现http请求的示例代码
- 2023-01-24 343浏览
-
- 一文详解Golang中net/http包的实现原理
- 2022-12-29 419浏览