手把手带你写一个 Web 框架
叶剑峰
腾讯高级工程师,前滴滴技术专家
22731 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 42 讲
特别放送 (1讲)
手把手带你写一个 Web 框架
15
15
1.0x
00:00/00:00
登录|注册

03|路由:如何让请求更快寻找到目标函数?

扩展多层嵌套通用前缀的路由
启动服务
定义包含有静态路由、批量通用前缀、动态路由的路由规则
将“增加路由规则”和“查找路由”添加到框架中
编写函数:“查找路由”
编写函数:“增加路由规则”
定义树和节点的数据结构
实现 Group 结构
定义 IGroup 接口
使用接口来替代结构定义
填充ServeHTTP 方法
匹配路由
注册路由
定义路由map
动态路由匹配
批量通用前缀
静态路由匹配
HTTP方法匹配
框架设计者偏好的研发风格
框架设计者希望使用者如何用路由模块
制定匹配规则
匹配查找出对应的控制器
让 Web 服务器根据规则理解 HTTP 请求中的信息
代表控制器的函数
输入和输出都是固定的
封装请求结构和返回结构
思考题
验证
实现动态路由匹配
实现批量通用前缀
实现HTTP方法和静态路由匹配
路由规则的需求
设计感
路由
控制器的方法结构
控制器简化为带有一个参数的函数
框架的Context
路由设计思路

该思维导图由 AI 生成,仅供参考

你好,我是轩脉刃。
上一讲,我们封装了框架的 Context, 将请求结构 request 和返回结构 responseWriter 都封装在 Context 中。利用这个 Context, 我们将控制器简化为带有一个参数的函数 FooControllerHandler,这个控制器函数的输入和输出都是固定的。在框架层面,我们也定义了对应关于控制器的方法结构 ControllerHandler 来代表这类控制器的函数。
每一个请求逻辑,都有一个控制器 ControllerHandler 与之对应。那么一个请求,如何查找到指定的控制器呢?这就是今天要研究的内容:路由,我将带你理解路由,并且实现一个高效、易用的路由模块。

路由设计思路

相信你对路由是干啥的已经有大致了解,具体来说就是让 Web 服务器根据规则,理解 HTTP 请求中的信息,匹配查找出对应的控制器,再将请求传递给控制器执行业务逻辑,简单来说就是制定匹配规则
但是就是这么简单的功能,路由的设计感不同,可用性有天壤之别。为什么这么说呢,我们带着这个问题,先来梳理一下制定路由规则需要的信息。
路由可以使用 HTTP 请求体中的哪些信息,得回顾我们第一节课讲 HTTP 的内容。
一个 HTTP 请求包含请求头和请求体。请求体内一般存放的是请求的业务数据,是基于具体控制业务需要的,所以,我们不会用来做路由。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了路由设计的思路和需求,强调了设计感的重要性,以及路由规则的设计与框架开发风格的直接关联。作者列举了路由规则的需求,包括HTTP方法匹配、静态路由匹配、批量通用前缀和动态路由匹配,并通过代码示例展示了注册路由规则的方式。文章还介绍了使用接口来替代结构定义,以提高代码的扩展性。特别值得一提的是,作者详细介绍了动态路由匹配的实现思路,使用trie树作为数据结构来支持路由匹配,并给出了具体的代码实现。通过本文的阅读,读者能够深入了解路由模块实现的细节,对于想要深入了解路由设计的技术人员来说,是一篇非常有价值的文章。文章通过实例展示了满足四个需求的路由实现,强调了思路比代码实现更重要,引发读者对代码设计感的思考。同时,作者提出了思考题,引导读者思考如何实现多层嵌套通用前缀的路由,增加了互动性和学习深度。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 Web 框架》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(21)

  • 最新
  • 精选
  • 好家庭
    为IGroup实现如下接口: Group(string) IGroup 类似于builder设计模式,可以链式调用

    作者回复: 你好,是的,这种方式支持链式调用,使用起来比较舒服。

    2021-09-17
    7
  • 路漫漫其修远兮
    两种路由方式是不是可以看成一类,底层存储不同?动态是指动态根据uri添加和获取handler。另外这种动态是否有必要去做,毕竟map是O(1)

    作者回复: 没办法的,动态路由确实有使用场景,特别对restapi风格比较执着的人会非常需要动态路由的,所以现在做框架基本都要求支持动态路由。当然,如果纯从性能考虑那是另外一个了

    2021-12-07
    2
  • 仔细思考了下这个trie🌲,路由如果是 /user/login 切割之后 会出现一层segment 为 空字符串 这样会浪费一些空间 这种空应该用不到吧 我觉得可以处理下 不知道老师怎么看 当前第0层 segment= 当前第1层 segment= 当前第2层 segment= USER 试着写了一个多叉树的打印,第0层是 tree.root 确实为空 但是第一层也为空

    作者回复: 思路ok的,可以节省一层。

    2021-11-30
    2
    2
  • 小然
    文中的树的图结构是否问题呢,按照代码实际生成的每一颗trie树root节点下面第一个子节点实际上是segment为空的节点,然后在这个节点下才是各个一级路劲的子节点。是我理解错误吗?我带着文中的树图结构去看代码添加路由算法看起来有很大的偏差,脑袋里想象纠正,先在root节点下先加一个segment为空的节点就好理解了。

    作者回复: 是的,这里主要是由于 segments := strings.Split(uri, "/") 会产生出第一个空节点。我在文章图中补充了一下描述。 谢谢提醒。

    2021-09-17
    3
    2
  • 邹志鹏.Joey ⁷⁷⁷
    child 的复数形式是 children, 希望可以改下

    作者回复: 谢谢

    2023-01-31归属地:广东
    1
  • 我是熊大
    看了三章觉得作者对于GO的使用,或者说是架构设计已经炉火纯青,细节很到位

    作者回复: 感谢

    2022-11-28归属地:北京
    1
  • 咸鱼
    有个小问题想咨询一下老师,filterChildNodes这个函数是否可以只返回一个*node,在什么情况下会返回多个*node?感觉按照约束条件来看,filterChildNodes只有可能返回一个*node

    作者回复: 如果有通配符,那么就有可能有多个,比如路由:/foo/*/bar和 /foo/bar, 请求url为foo/bar,当查询第二个bar的时候,就有可能匹配到2个的

    2021-12-11
    2
    1
  • 老师的字典树思路巨清晰,之后重构的时候自己重新把字典树写了 都有点模糊了 期待后续文件 感谢老师分享

    作者回复: 同感谢

    2021-11-30
    1
  • 刷了一个月的leetcode 让我的第一反应是字典树 应该没说错

    作者回复: 是的

    2021-11-30
  • 木小柒
    /user/name /user/:id 一开始看这个,想找个冲突挺奇怪的,这个感觉还算正常,才发觉原来这里 :id 类似 gin 里面的 *id

    作者回复: 是的,在gin中也有:id, 代表有一个,而*代表匹配所有。

    2021-09-21
收起评论
显示
设置
留言
21
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部