js路由参数获取的秘密,你知道吗?只需三步!

这篇文章我们来手动实现一个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

正则路由参数获取

这个有些难度,我们分几步来做

  1. 将参数路由用正则表达式表示出来
  2. 然后用这个正则表达式来识别正确的路径
  3. 路径匹配之后,将路径中的参数提取出来
// 定义一个函数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 作者:慢功夫

(0)
上一篇 2023年5月1日 上午10:36
下一篇 2023年5月1日 上午10:46

相关推荐

发表回复

登录后才能评论