浏览器
双核浏览器
游戏浏览器
高速浏览器
视频浏览器
IE浏览器
浏览器周边
手机浏览器
安卓游戏
音乐舞蹈
养成游戏
儿童游戏
仙侠手游
角色扮演
策略塔防
休闲益智
动作冒险
飞行射击
模拟经营
体育运动
赛车竞速
网络游戏
安卓软件
资讯阅读
生活服务
影音播放
购物理财
效率办公
趣味娱乐
交通出行
学习教育
摄影图像
在线音乐
系统工具
网络购物
聊天工具
安全杀毒
图片编辑
新闻资讯
软件资讯
游戏资讯
手机教程
手游攻略
游戏攻略
软件教程
IE专区
安卓专题
文章合集
电脑软件专题
安全软件
杀毒软件
系统安全
加密解密
防火墙
远程控制
木马查杀
影音软件
网络电视
视频播放
音乐播放
视频制作
音频编辑
录音录像
教育学习
外语学习
教育管理
早教启蒙
在线课堂
成人教育
资格考试
聊天社交
即时通讯
视频聊天
在线交友
变声器
表情包
办公软件
线上会议
文档管理
行业管理
考勤打卡
应用工具
输入法
下载工具
时钟日历
记事本
文件管理
计算器
编程开发
编程工具
JAVA相关
加壳脱壳
编程控件
数据库
网页源码
软件开发
补丁制作
系统美化
桌面制作
壁纸大全
系统主题
屏幕保护
桌面辅助
系统软件
系统优化
备份还原
系统检测
U盘工具
磁盘工具
驱动补丁
图文处理
图片素材
图像处理
图片制作
图片压缩
电子相册
抓图工具
其他软件
电子书籍
模拟器
辅助工具
交通出行
手机管理
其他工具
素材下载
字体素材
PPT素材
专区
文章合集
软件
游戏
浏览器
安卓专题
软件
游戏
浏览器
IEfans/ IE专区/ IE相关 /玩转Koa之koa-router原理解析

玩转Koa之koa-router原理解析

2023-02-27 17:04:01 编辑:匿名

一、前言

Koa为了保持自身的简洁,并没有捆绑中间件。但是在实际的开发中,我们需要和形形色色的中间件打交道,本文将要分析的是经常用到的路由中间件 -- koa-router。

如果你对Koa的原理还不了解的话,可以先查看Koa原理解析。

二、koa-router概述

koa-router的源码只有两个文件:router.js和layer.js,分别对应Router对象和Layer对象。

Layer对象是对单个路由的管理,其中包含的信息有路由路径(path)、路由请求方法(method)和路由执行函数(middleware),并且提供路由的验证以及params参数解析的方法。

相比较Layer对象,Router对象则是对所有注册路由的统一处理,并且它的API是面向开发者的。

接下来从以下几个方面全面解析koa-router的实现原理:

Layer对象的实现

路由注册

路由匹配

路由执行流程

三、Layer

Layer对象主要是对单个路由的管理,是整个koa-router中最小的处理单元,后续模块的处理都离不开Layer中的方法,这正是首先介绍Layer的重要原因。

image.png

Layer构造函数主要用来初始化路由路径、路由请求方法数组、路由处理函数数组、路由正则表达式以及params参数信息数组,其中主要采用path-to-regexp方法根据路径字符串生成正则表达式,通过该正则表达式,可以实现路由的匹配以及params参数的捕获:

image.png

根据paramNames中的参数信息以及captrues方法,可以获取到当前路由params参数的键值对:

image.png

需要注意上述代码中的safeDecodeURIComponent方法,为了避免服务器收到不可预知的请求,对于任何用户输入的作为URI部分的内容都需要采用encodeURIComponent进行转义,否则当用户输入的内容中含有'&'、'='、'?'等字符时,会出现预料之外的情况。而当我们获取URL上的参数时,则需要通过decodeURIComponent进行解码,而decodeURIComponent只能解码由encodeURIComponent方法或者类似方法编码,如果编码方法不符合要求,decodeURIComponent则会抛出URIError,所以作者在这里对该方法进行了安全化的处理:

image.png

Layer还提供了对于单个param前置处理的方法:

image.png

上述代码中通过some方法寻找单个param处理函数的原因在于以下两点:

保持param处理函数位于其他路由处理函数的前面;

路由中存在多个param参数,需要保持param处理函数的前后顺序。

image.png

Layer中的setPrefix方法用于设置路由路径的前缀,这在嵌套路由的实现中尤其重要。

最后,Layer还提供了根据路由生成url的方法,主要采用path-to-regexp的compile和parse对路由路径中的param进行替换,而在拼接query的环节,正如前面所说需要对键值对进行繁琐的encodeURIComponent操作,作者采用了urijs提供的简洁api进行处理。

四、路由注册

1、Router构造函数

首先看了解一下Router构造函数:

image.png

在构造函数中初始化的params和stack属性最为重要,前者用来保存param前置处理函数,后者用来保存实例化的Layer对象。并且这两个属性与接下来要讲的路由注册息息相关。

koa-router中提供两种方式注册路由:

具体的HTTP动词注册方式,例如:router.get('/users', ctx => {})

支持所有的HTTP动词注册方式,例如:router.all('/users', ctx => {})

2、http METHODS

源码中采用methods模块获取HTTP请求方法名,该模块内部实现主要依赖于http模块:

image.png

3、router.verb() and router.all()

这两种注册路由的方式的内部实现基本类似,下面以router.verb()的源码为例:

image.png

该方法第一部分是对传入参数的处理,对于middleware参数的处理会让大家联想到ES6中的rest参数,但是rest参数与arguments其中一个致命的区别:

rest参数只包含那些没有对应形参的实参,而arguments则包含传给函数的所有实参。

如果采用rest参数的方式,上述函数则必须要求开发者传入name参数。但是也可以将name和path参数整合成对象,再结合rest参数:

image.png

采用ES6的新特性,代码变得简洁多了。

第二部分是register方法,传入的method参数的形式就是router.verb()与router.all()的最大区别,在router.verb()中传入的method是单个方法,后者则是以数组的形式传入HTTP所有的请求方法,所以对于这两种注册方法的实现,本质上是没有区别的。

4、register

image.png

register方法主要负责实例化Layer对象、更新路由前缀和前置param处理函数,这些操作在Layer中已经提及过,相信大家应该轻车熟路了。

5、use

熟悉Koa的同学都知道use是用来注册中间件的方法,相比较Koa中的全局中间件,koa-router的中间件则是路由级别的。

Router.prototype.use = function () {

image.png

koa-router中间件注册方法主要完成两项功能:

将路由嵌套结构扁平化,其中涉及到路由路径的更新和param前置处理函数的插入;

路由级别中间件通过注册一个没有method的Layer实例进行管理。

五、路由匹配

image.png

match方法主要通过layer.match方法以及methods属性对layer进行筛选,返回的matched对象包含以下几个部分:

path: 保存所有路由路径被匹配的layer;

pathAndMethod: 在路由路径被匹配的前提下,保存路由级别中间件和路由请求方法被匹配的layer;

route: 仅当存在路由路径和路由请求方法都被匹配的layer,才能算是本次路由被匹配上。

另外,在ES7之前,对于判断数组是否包含一个元素,都需要通过indexOf方法来实现, 而该方法返回元素的下标,这样就不得不通过与-1的比较得到布尔值:

image.png

而作者巧妙地利用位运算省去了“讨厌的-1”,当然在ES7中可以愉快地使用includes方法:

image.png

六、路由执行流程

理解koa-router中路由的概念以及路由注册的方式,接下来就是如何作为一个中间件在koa中执行。

koa中注册koa-router中间件的方式如下:

image.png

从代码中可以看出koa-router提供了两个中间件方法:routes和allowedMethods。

1、allowedMethods()

image.png

allowedMethods()中间件主要用于处理options请求,响应405和501状态。上述代码中的ctx.matched中保存的正是前面matched对象中的path(在routes方法中设置,后面会提到。),在matched对象中的path数组不为空的前提条件下:

服务器不支持当前请求方法,返回501状态码;

当前请求方法为OPTIONS,返回200状态码;

path中的layer不支持该方法,返回405状态;

对于上述三种情况,服务器都会设置Allow响应头,返回该路由路径上支持的请求方法。

2、routes()

image.png

routes()中间件主要实现了四大功能。

将matched对象的path属性挂载在ctx.matched上,提供给后续的allowedMethods中间件使用。(见代码中的【1】)

将返回的dispatch函数设置router属性,以便在前面提到的Router.prototype.use方法中区别路由级别中间件和嵌套路由。(见代码中的【2】)

插入一个新的路由前置处理中间件,将layer解析出来的params对象、路由别名以及捕获数组挂载在ctx上下文中,这种操作同理Koa在处理请求之前先构建context对象。(见代码中的【3】)

而对于路由匹配到众多layer,koa-router通过koa-compose进行处理,这和koa对于中间件处理的方式一样的,所以koa-router完全就是一个小型洋葱模型。

七、总结

koa-router虽然是koa的一个中间件,但是其内部也包含众多的中间件,这些中间件通过Layer对象根据路由路径的不同进行划分,使得它们不再像koa的中间件那样每次请求都执行,而是针对每次请求采用match方法匹配出相应的中间件,再利用koa-compose形成一个中间件执行链。

相关推荐

浏览器更新