koa-router

Posted by Leo on 2022-03-10
Estimated Reading Time 3 Minutes
Words 720 In Total
Viewed Times

问题描述

在使用 Koa-router 作为路由遇到了一个优先级问题.如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// routerPage.js file
const router = require("koa-router");
router.get("/test", (ctx) => {
ctx.body = "test";
});
router.get("/router/test", (ctx) => {
ctx.body = "router test";
});
module.exports = router;

// routerIndex.js file
const router = require("koa-router");
const routerPage = require("./routerPage");
router.use(routerPage.routes(), routerPage.allowedMethods());
module.exports = router;

在访问"/router/test"时路由会优先匹配到"/test"路由,返回ctx.body = "test",这个问题就很尴尬了,项目空闲下来去翻看源码终于找到了原因

问题原因

Koa-router 的源码并不长,layer.js 和 router.js 两个文件加起来共一千多行代码.建议可以结合这篇文章阅读.
其中造成这个问题的原因就是 router.js 中router.use这个方法,方法源码如下

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
29
30
31
32
33
34
35
36
37
38
39
40
// 主要作用: 给path添加中间件
Router.prototype.use = function () {
var router = this;
var middleware = Array.prototype.slice.call(arguments);
var path = "(.*)";

// 如果path为array则递归调用use方法
if (Array.isArray(middleware[0]) && typeof middleware[0][0] === "string") {
middleware[0].forEach(function (p) {
router.use.apply(router, [p].concat(middleware.slice(1)));
});

return this;
}
//如果传入了path,则只对此path操作
var hasPath = typeof middleware[0] === "string";
if (hasPath) {
path = middleware.shift();
}
// 如果传入参数为一个路由数组,则遍历为每个路由添加前缀,中间件,并将此路由放入全局的路由数组
middleware.forEach(function (m) {
if (m.router) {
m.router.stack.forEach(function (nestedLayer) {
if (path) nestedLayer.setPrefix(path);
if (router.opts.prefix) nestedLayer.setPrefix(router.opts.prefix);
router.stack.push(nestedLayer);
});

if (router.params) {
Object.keys(router.params).forEach(function (key) {
m.router.param(key, router.params[key]);
});
}
} else {
router.register(path, [], m, { end: false, ignoreCaptures: !hasPath });
}
});

return this;
};

问题就出在router.use(routerPage.routes(), routerPage.allowedMethods())时没有设置前缀,
路由就自动添加了默认的前缀"(.*)",这里的 path 发生了改变,在路由后续的操作中,将 path 使用pathToRegExp转换成正则表达式时"/test"这个 path 本应该是/^\/test...../就会变成/(.*)/\/test...(大概是这个意思)
那么原本以/test开头的路由就会匹配包含/test的路由
所以 request path 为/router/test时会被/test路由先匹配中,路由也就不会往下匹配

解决方式

  • 将条件更精确的路由放到前面
  • /test那个路由中加一个中间件,当匹配到/router/testawait next()继续向下执行
  • 更改源码Router.propertype.usepath = "(.*)"path = false
  • 在使用router.use时代码做一定更改,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// routerPage.js file
const router = require("koa-router");
router.get("test", (ctx) => {
ctx.body = "test";
});
router.get("router/test", (ctx) => {
ctx.body = "router test";
});
module.exports = router;

// routerIndex.js file
const router = require("koa-router");
const routerPage = require("./routerPage");
router.use("/", routerPage.routes(), routerPage.allowedMethods());
module.exports = router;

如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !