这篇文章我们来手动实现一个express router 的路由校验。
express路由支持的正则表达式
express的路由支持各种正则表达式
?:问号之前的一个字符只能出现零次或一次。例如,路由路径 ‘/ab?cd’ 路径匹配端点 acd 或 abcd。
+:加号之前的一个字符至少出现一次。例如,路径路径 ‘/ab+cd’ 匹配端点 abcd、abbcd、abbbcd,等。
:星号可以替换为任意字符串。例如,路由路径 ‘/abcd’ 匹配端点 abcd、abXcd、abSOMErandomTEXTcd,等。
():将一个字符串视为一体以执行 ?、+、* 操作。例如。 ‘/ab(cd)?e’ 将对 (cd) 进行匹配,将匹配到 abe 和 abcde。
这些都比较好实现,但它还支持一个功能–路由参数
举例说,一个包含用户和藏书信息的 URL:http://localhost:3000/users/34/books/8989
,可以这样提取信息(使用 userId 和 bookId 路径参数):
rule: '/users/:userId/books/:bookId';
input: '/users/34/books/8989';
output: {
userId: 34,
bookId: 8989,
}
这个怎么做呢,还是有些难度的吧。不着急,我们慢慢来,先从简单的开始
正则路由的校验
// 定义一个函数check,接受两个字符串参数rule和pathname
const check2 = (rule, pathname) => {
// 用rule创建一个正则表达式对象ruleRegExp,注意用^和$表示字符串的开始和结束
const ruleRegExp = new RegExp(`^${rule}$`);
// 用pathname去匹配ruleRegExp,如果匹配成功就返回true,否则返回false
return pathname.match(ruleRegExp) ? true : false;
};
这段代码定义了一个函数check,它接受两个参数rule和pathname,然后用rule创建一个正则表达式对象ruleRegExp,再用pathname去匹配这个正则表达式,如果匹配成功就返回true,否则返回false。
// 打印check2函数的调用结果,第一个参数是规则,第二个参数是要检查的路径
console.log(check2("/getName+", "/getName"));
// false,因为规则要求路径以一个或多个e结尾,而实际路径只有一个e
console.log(check2("/getName+", "/getNamee"));
// true,因为规则和实际路径完全一致
console.log(check2("/getName+", "/getNam"));
// false,因为规则要求路径以一个或多个e结尾,而实际路径没有e
正则路由参数获取
这个有些难度,我们分几步来做
- 将参数路由用正则表达式表示出来
- 然后用这个正则表达式来识别正确的路径
- 路径匹配之后,将路径中的参数提取出来
// 定义一个函数check,接受两个字符串参数rule和pathname
const check = (rule, pathname) => {
// 定义一个正则表达式paraRegExpStr,用于匹配rule中的参数部分,例如:name或:age
const paraRegExpStr = /:[^/]+/g;
// 用replace方法把rule中的参数部分替换成`([^/]+)`,得到一个新的字符串ruleRegExpStr
// `([^/]+)`表示匹配任意个非/的字符,并捕获它们
const ruleRegExpStr = rule.replace(paraRegExpStr, `([^/]+)`);
// 用ruleRegExpStr创建一个正则表达式对象ruleMatchRegExp,注意用^和$表示字符串的开始和结束
const ruleMatchRegExp = new RegExp(`^${ruleRegExpStr}$`);
// 用test方法匹配ruleMatchRegExp,返回一个布尔值
return ruleMatchRegExp.test(pathname);
};
这段代码定义了一个函数check,它接受两个参数rule和pathname,然后用paraRegExpStr匹配rule中的路由参数部分,例如:name或:age,然后用**([^/]+)**替换这些参数部分,得到一个新的字符串ruleRegExpStr,再用这个字符串创建一个正则表达式对象ruleMatchRegExp,再用pathname去匹配这个正则表达式,返回一个布尔值。
创建出正确的路径正则表达式是重点
// 打印check函数的调用结果,第一个参数是规则,第二个参数是要检查的路径
console.log(check("/getName/:name", "/getName/zenos"));
// true,因为规则和实际路径匹配,并且捕获了zenos作为name参数的值
console.log(check("/getAge/:age", "/getAge/18"));
// true,因为规则和实际路径匹配,并且捕获了18作为age参数的值
console.log(check("/getAge/:age", "/getAge/18/12"));
// false,因为规则和实际路径不匹配,多了一个/12部分
第1步和第2步已经完成了,下面就开始做参数的提取了。
// 定义一个函数check,接受两个字符串参数rule和pathname
const check = (rule, pathname) => {
// 用replace方法把rule中的参数部分替换成`([^/]+)`,得到一个新的字符串ruleRegExpStr
// `([^/]+)`表示匹配任意个非/的字符,并捕获它们
const ruleRegExpStr = rule.replace(/:[^/]+/g, `([^/]+)`);
// 用ruleRegExpStr创建一个正则表达式对象ruleMatchRegExp,注意用^和$表示字符串的开始和结束
const ruleMatchRegExp = new RegExp(`^${ruleRegExpStr}$`);
// 用match方法匹配ruleMatchRegExp,得到一个数组或null
const ruleMatched = pathname.match(ruleMatchRegExp);
// 用match方法匹配rule中的参数部分,例如:name或:age,得到一个数组或null
const paraMatched = rule.match(/:[^/]+/g);
// 如果ruleMatched不是null,说明匹配成功
if (ruleMatched) {
// 创建一个空对象res,用于存储参数名和参数值的映射关系
const res = {};
// 如果paraMatched不是null,说明规则中有参数部分
if (paraMatched) {
// 定义一个变量index,表示参数值在ruleMatched中的索引位置,从1开始
let index = 1;
// 遍历paraMatched中的参数名
for (let key of paraMatched) {
// 把参数名去掉冒号后作为res的属性名,把ruleMatched中对应的参数值作为res的属性值
res[key.slice(1)] = ruleMatched[index++];
}
}
// 返回res对象
return res;
}
// 否则返回null
return null;
};
这段代码定义了一个函数check,它接受两个参数rule和pathname,先用**([^/]+)**替换rule的路由参数部分,得到一个新的字符串ruleRegExpStr,再用这个字符串创建一个正则表达式对象ruleMatchRegExp,再用pathname去匹配这个正则表达式,得到一个数组或null。
如果匹配成功,就创建一个空对象res,然后遍历paraMatched中的参数名,把它们作为res的属性名,把ruleMatched中对应的参数值作为res的属性值,最后返回res。
如果匹配失败,就返回null
// 打印check函数的调用结果,第一个参数是规则,第二个参数是要检查的路径
console.log(check("/getName/:name", "/getName/zenos"));
// { name: 'zenos' },因为规则和实际路径匹配,并且捕获了zenos作为name参数的值
console.log(check("/getAge/:age", "/getAge/18"));
// { age: '18' },因为规则和实际路径匹配,并且捕获了18作为age参数的值
console.log(check("/getInfo/:name/:age", "/getInfo/zenos/18"));
// { name: 'zenos', age: '18' },
// 因为规则和实际路径匹配,并且捕获了zenos作为name参数的值和18作为age参数的值
console.log(check("/getAge/:age", "/getAge/18/12"));
// null,因为规则和实际路径不匹配,多了一个/12部分
成功
参数的获取中,路径名称也支持正则表达式哦
总结:
这篇文章是一场js之旅,带你探索express route的正则路由校验,以及如何获取路由参数的奥秘。我用了三个步骤,让你一目了然,感受到了代码的魅力。 下一站,我将用这个路由校验,亲手打造一个express路由中间件。中间件的原理会涉及到这篇文章(手写洋葱模型),这也是一篇美妙的文章,期待你的阅读。
原文链接:https://juejin.cn/post/7225129878660087866 作者:慢功夫