Node.js 封装仿照 express 的路由

我们想要仿照express来封装我们的路由,我们先来看看一个仿照express封装的一个简单路由demo,这有助于我们了解express,然后我们会一步步来实现我们自己的简单express路由。

主入口:

//index.js
var route = require('./model/http-route.js');
var app = route();
var http = require('http');
var ejs=require('ejs');
var server = http.createServer(app);

app.get('/', function (req, res) {

    res.send('首页');
});
app.get('/login', function (req, res) {

    res.send('login');
});

app.get('/register', function (req, res) {

    res.send('register');

});

app.get('/dologin', function (req, res) {
     ejs.renderFile('views/form.ejs',{},function(err,data){
            res.end(data);

     });

});

app.post('/test', function (req, res) {
    console.log('POST', req.query);
    res.send(req.query);
});
server.listen(8080, function () {
    console.log('listen ' + server.address().port);
});

http-route.js

var url = require('url');

/**
 * 对resquest进行封装
 *
 * @param {*} res
 */
var packingRes = function (res) {
    var end = res.end;
    res.end = function (data, encoding, callback) {
        if (data && !(data instanceof Buffer) && (typeof data !== 'function')) {
            if (typeof data === 'object') {
                data = JSON.stringify(data);
            } else if (typeof data === 'number') {
                data = data.toString();
            }
        }

        end.call(res, data, encoding, callback);
    };

    res.send = function (data, type) {
        res.writeHead(200,
            {
                'Access-Control-Allow-Origin': '*',
                'Content-Type': 'text/' + (type || 'plain') + '; charset=UTF-8'
            }
        );
        res.end(data);
    };

    res.sendImg = function (data, type, timeout) {
        res.writeHead(200,
            {
                'Access-Control-Allow-Origin': '*',
                'Content-Type': 'image/' + (type || 'png'),
                'Content-Length': Buffer.byteLength(data),
                'Cache-Control': 'max-age=' + (timeout || 5184000)
            }
        );
        res.end(data);
    };

    return res;
};

/**
 * 路由规则
 */
var route = function () {
    var self = this;

    this._get = {};
    this._post = {};

    /**
     * 处理请求
     *
     * @param {*} req
     * @param {*} res
     */
    var handle = function (req, res) {
        packingRes(res);

        var Url = url.parse(req.url, true);
        var pathname = Url.pathname;
        if (!pathname.endsWith('/')) {
            pathname = pathname + '/';
        }
        var query = Url.query;
        var method = req.method.toLowerCase();

        if (self['_' + method][pathname]) {
            if (method == 'post') {
                // 设置接收数据编码格式为 UTF-8
                // req.setEncoding('utf-8');
                var postData = '';
                // 数据块接收中
                req.on('data', function (postDataChunk) {
                    postData += postDataChunk;
                });
                // 数据接收完毕,执行回调函数
                req.on('end', function () {
                    try {
                        postData = JSON.parse(postData);
                    } catch (e) { }
                    req.query = postData;
                    self['_' + method][pathname](req, res);
                });
            } else {
                req.query = query;
                self['_' + method][pathname](req, res);
            }
        } else {
            res.send(method + '请求路由地址不存在:' + pathname);
        }
    };

    /**
     * 注册get请求
     */
    handle.get = function (string, callback) {
        if (!string.startsWith('/')) {
            string = '/' + string;
        }
        if (!string.endsWith('/')) {
            string = string + '/';
        }
        self._get[string] = callback;
    };

    /**
     * 注册post请求
     */
    handle.post = function (string, callback) {
        if (!string.startsWith('/')) {
            string = '/' + string;
        }
        if (!string.endsWith('/')) {
            string = string + '/';
        }
        self._post[string] = callback;
    };

    return handle;
};

module.exports = function () {
    return new route();
};

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

我们现在开始封装自己的express。

我们先来写一个例子:

var app = function(){

    console.log('app');

}

app.get=function(){
    console.log('app.get');
}
app.post=function(){
    console.log('app.post');
}


app.post(); /*app.post*/

//
app();   /*app*/


console.log(app);

这里写图片描述


利用上面,我们可以分别来封装我们的get和post方式。
当请求是get的时候,我们执行app.get(),当请求是post的时候,我们执行app.post()。
但是光区分get和post方式是不够的,我们还需要根据不同的请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。 所以我们还需要注册路由,根据不同的URL来找到对应的路由。

var G={};


var app=function(req,res){
    //console.log('app'+req);

    if(G['login']){
        G['login'](req,res);  /*执行注册的方法*/
    }
}

//定义一个get方法
app.get=function(string,callback){
    G[string]=callback;

    //注册方法
    //G['login']=function(req,res){
    //
    //}
}

//执行get方法
app.get('login',function(req,res){
    console.log('login '+req);
})


setTimeout(function(){
    app('req','res');
},1000);

我们先定义了一个对象G,跟之前一样我们还定义了app 方法,还有get方法。

我们先来看get方法

//定义一个get方法
app.get=function(string,callback){
    G[string]=callback;
}

get方法的参数分别为,string是注册的路由名,比如’login’,当你请求/login的时候,他就会找到对应的login路由,callback是回调函数,就是具体路由内容。
我们再来看如何去注册一个路由。

app.get('login',function(req,res){
    console.log('login '+req);
})

我们调用了app.get方法。给它传了一个login的路由名,和一个回调函数。输出了一段文字。
上面这一段的效果相当于:

app.get=function(string,callback){
    G[string]=callback;

    //注册方法
    //G['login']=function(req,res){
    //
    //}
}

给G对象添加了一个login方法。就像我们之前写的路由一样:

module.exports = {

    login : function(req,res){
         console.log('login '+req);
    },

}

我们再来看看app方法,这是路由的入口,也是程序的入口,根据不同的URL信息来找到对应的路由方法。

var app=function(req,res){
    //console.log('app'+req);

    if(G['login']){
        G['login'](req,res);  /*执行注册的方法*/
    }
}

我们这里直接写死了请求login路由,实际上我们可以从request中获得url,得到我们要请求的路由,然后使用

 if(G[routerName]){
      G[routerName](req,res);  /*执行注册的方法*/
 }

最后我们只需要执行:

setTimeout(function(){
    app('req','res');
},1000);

这里写图片描述


我们继续改造我们的express,我们要把他放到服务器上去,然后要根据我们访问url,去请求指定的路由。

var http=require('http');

var url=require('url');
var G={};

//定义方法开始结束
var app=function(req,res){
    //console.log('app'+req);

    var pathname=url.parse(req.url).pathname;

    if(!pathname.endsWith('/')){

        pathname=pathname+'/';
    }

    if(G[pathname]){
        G[pathname](req,res);  /*执行注册的方法*/
    }else{

        res.end('no router');
    }
}

//定义一个get方法
app.get=function(string,callback){


    if(!string.endsWith('/')){
        string=string+'/';

    }
    if(!string.startsWith('/')){
        string='/'+string;

    }

    //    /login/
    G[string]=callback;

    //注册方法
    //G['login']=function(req,res){
    //
    //}
}



//只有有请求 就会触发app这个方法
http.createServer(app).listen(3000);

//注册login这个路由(方法)
app.get('login',function(req,res){

        console.log('login');
        res.end('login');
})

app.get('register',function(req,res){

    console.log('register');
    res.end('register');
})

我们来看app方法:

//定义方法开始结束
var app=function(req,res){
    //console.log('app'+req);

    var pathname=url.parse(req.url).pathname;

    if(!pathname.endsWith('/')){

        pathname=pathname+'/';
    }

    if(G[pathname]){
        G[pathname](req,res);  /*执行注册的方法*/
    }else{

        res.end('no router');
    }
}

这次我们获取了请求request的url,然后解析获取了请求的pathname,根据pathname去找注册了的路由。
get方法还是和之前的类似:

//定义一个get方法
app.get=function(string,callback){


    if(!string.endsWith('/')){
        string=string+'/';

    }
    if(!string.startsWith('/')){
        string='/'+string;

    }

    //    /login/
    G[string]=callback;

    //注册方法
    //G['login']=function(req,res){
    //
    //}
}

然后我们注册了两个路由方法:

//注册login这个路由(方法)
app.get('login',function(req,res){

        console.log('login');
        res.end('login');
})

app.get('register',function(req,res){

    console.log('register');
    res.end('register');
})

接下来就是创建一个Server:

//只有有请求 就会触发app这个方法
http.createServer(app).listen(3000);

上面的代码相当于:

http.createServer(function(req,res){
    //console.log('app'+req);

    var pathname=url.parse(req.url).pathname;

    if(!pathname.endsWith('/')){

        pathname=pathname+'/';
    }

    if(G[pathname]){
        G[pathname](req,res);  /*执行注册的方法*/
    }else{

        res.end('no router');
    }
}).listen(3000);

这里写图片描述

这里写图片描述

好了,我们的目标基本实现了,现在我们需要再进一步封装我们的代码,把一些代码提出来封装成一个模块。
我们把它封装成为express-route.js


var url=require('url');

//封装方法改变res  绑定res.send()
function changeRes(res){

    res.send=function(data){

        res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});

        res.end(data);
    }
}

//暴露的模块
var Server=function(){


    var G=this;   /*全局变量*/

    //处理get和post请求
    this._get={};

    this._post={};



    var app=function(req,res){


        changeRes(res);

        //获取路由
        var pathname=url.parse(req.url).pathname;
        if(!pathname.endsWith('/')){
            pathname=pathname+'/';
        }

        //获取请求的方式 get  post
        var method=req.method.toLowerCase();


        if(G['_'+method][pathname]){

            if(method=='post'){ /*执行post请求*/

                var postStr='';
                req.on('data',function(chunk){

                    postStr+=chunk;
                })
                req.on('end',function(err,chunk) {

                    req.body=postStr;  /*表示拿到post的值*/


                    //G._post['dologin'](req,res)

                    G['_'+method][pathname](req,res); /*执行方法*/

                })



            }else{ /*执行get请求*/
                G['_'+method][pathname](req,res); /*执行方法*/

            }

        }else{

            res.end('no router');
        }

    }

    app.get=function(string,callback){
        if(!string.endsWith('/')){
            string=string+'/';
        }
        if(!string.startsWith('/')){
            string='/'+string;

        }

        //    /login/
        G._get[string]=callback;

    }

    app.post=function(string,callback){
        if(!string.endsWith('/')){
            string=string+'/';
        }
        if(!string.startsWith('/')){
            string='/'+string;

        }
        //    /login/
        G._post[string]=callback;

        //G._post['dologin']=function(req,res){
        //
        //}
    }

    return app;

}

module.exports=Server();

主程序:

var http=require('http');

var ejs=require('ejs');

var app=require('./model/express-route.js');

console.log(app);

http.createServer(app).listen(3000);

app.get('/',function(req,res){

    var msg='这是数据库的数据'

    ejs.renderFile('views/index.ejs',{msg:msg},function(err,data){

        res.send(data);
    })
})


//登录页面
app.get('/login',function(req,res){

    console.log('login');

    ejs.renderFile('views/form.ejs',{},function(err,data){

        res.send(data);
    })

})

//执行登录
app.post('/dologin',function(req,res){

    console.log(req.body);  /*获取post传过来的数据*/

    res.send("<script>alert('登录成功');history.back();</script>")
})


app.get('/register',function(req,res){

    console.log('register');

    res.send('register');
})

app.get('/news',function(req,res){

    console.log('register');

    res.send('新闻数据');
})

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

好了我们自己的express也封装好了,通过上面的我们可以更加容易地理解express框架了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值