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.jspathRegexp.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的属性,也是必须具备的,否则就无法实际开发。下一节对今天开发的一些代码做一下总结,这样可以跟深入的理解开发细节。