第6天 路由功能——params.js 插件开发
params.js 插件开发
params.js 中间件可以把:xxx
路由解析出来的数据,保存到req.params中。但这个插件开发有些难度,原因是如何知道url和:xxx
对应?首先就要知道那个路由器,然后还要解析出对应的url。越想越复杂。所以,我们需要修改App.js代码,这个功能集成到泛式路由部分。
首先,需要对pathRegexp.js模块进行修改,通过修改可以知道某个路由规则下的参数名称。例如 /about/:name/:age 要得到一个数组 ["name","age"];
pathRegexp.js
修改后的代码是:
module.exports = pathRegexp; function pathRegexp(path) { var paramNames = []; path = path // 这个方法用把 * 替换成正则表达式的 [0-9a-zA-Z\-_]* 形式。 // 上一节介绍了,这里用了 [0-9a-zA-Z\-_]* 目的是一种限定。 // 如果用 .* 那么 /article/:name 就会有个bug,/article/ok/abc 也是匹配的 // 这是因为 .* 匹配的话 ok/abc 也是匹配 .* 的,因为 / 也符合。 .replace(/((\*{1}(?=\/))|(\*{1}(?=$)))/g, "[0-9a-zA-Z\-_]*") // 这个方法是把 :xxx 的形式替换成 [0-9a-zA-Z\-_]* 正则表达式形式。 // 这个方法还有个作用,就是例如:把 :name的,提取出 name ,并保存到paramNames数组中。 // 保存到paramNames中的目的是,当有匹配这个路由时,通过paramNames可以得到对应的值。 // 这也是为什么要采用 ([0-9a-zA-Z\-_]*) 的形式 ,而不是直接 [0-9a-zA-Z\-_]* 用这个, // 因为 ([0-9a-zA-Z\-_]*) 形式是正则表达式组的形式,通过通过正则表达式的 RegExp.$1 ... $n // 与paramNames就可得到参数名对应的值 ,例如: // /article/:id 的如果url是 /article/id001 ,那么就有办法得到 id="id001",因为paramNames数组 // 现在是 ["id"] ,那么,/article/:id 由这个方法转换后,转换为 /^\/article\/([0-9a-zA-Z\-_]*)\/?$/ // 这个形式。通过 /^\/article\/([0-9a-zA-Z\-_]*)\/?$/g.test("/article/id001") 得到true。 // 当正则表达式调用了test方法,那么RegExp表达式类的静态值$1 $2 ... 就会重写! // 那么,这里RegExp.$1值就是id001。而 paramNames[0] 的值是 id,这样就能得到 // id和对应的值。 // 具体实验代码如下: // /^\/article\/([0-9a-zA-Z\-_]*)\/?$/g.test("/article/id001"); // console.log(RegExp.$1); // 打印出 id001 .replace(/(:(.*?(?=\/)))|(:(.*?(?=$)))/g,function () { // arguments的第一个和最后两个参数,并不是我们想要的$1 ... $n的匹配值, // 所以 len 是匹配的数量 var len = arguments.length - 3; for (var i = 0; i < len; i++) { var avg = arguments[i + 1]; // 由于正则嵌套分组,所以要判断匹配字符串是否有 " : " 前缀, // 目的是得到 :name的name部分和 :age 的 age部分。 if (typeof avg === "string" && avg[0] !== ":") { paramNames.push(avg); } } return "([0-9a-zA-Z\-_]*)" }) // 把 /article/:id/ 的转换为 /article/:id .replace(/\/$/g, "") // 把 / 转换为 \/ ,因为这是字符串形式,最后通过 new RegExp(path) // 生成时,必须要经过这个转换。 .replace(/\//g, "\\\/") var regexp = new RegExp("^" + path + "\\/?$"); regexp.paramNames = paramNames; return regexp; }
paramNames这个数组中就储存着参数名称,然后直接赋值给regexp.paramNames = paramNames。下一步,要在App.js中做文章,把每次符合对应正则的请求url,解析出:xxx
对应的值。
首先,我们要在App.js中的handle函数里,加入 req.params = {} 用于储存,代码如下:
function App(){ // request事件响应函数 function handle(req,res){ req.params = {}; } }
最后,要在 App.js 中的findHandle函数内做文章,看下面代码。
function findHandle(route_handles){ if(pass){ // 得到URL中对应 :XXX 的值,并保存在req.params中。 route_handle.route.paramNames.forEach(function(name,index){ req.params[name] = RegExp["$"+(index+1)]; }) handle = route_handle.handle; break; } } }
通过这三个部分的代码,让stuwebfk框架具备了req.params特性。而本节题目是params.js插件,看来是错了,因为并没有建立params.js这个模块,而是为了实现这个功能,修改了 App.js
和 pathRegexp.js
。 这里有些JS语言相对高级的用法,稍后会在最后一节中,加以总结。
下面,通过视频的方式,演示此功能。
下面是测试代码:
var App = require("../..").App, query = require("../..").query, app = new App; app.get("/about/:name/:age",function(req,res){ res.write("my name is "+req.params.name+"\n"); res.write("my age is "+req.params.age) res.end(); }) app.listen(3000);
现在的框架具备了req.query和req.params的属性,也是必须具备的,否则就无法实际开发。下一节对今天开发的一些代码做一下总结,这样可以跟深入的理解开发细节。