终极目标:掌握和使用node

本博客目的:记录node学习的进度和心得

内容:Nodejs中的路由模块封装、封装仿照express的路由。

路由模块封装(模块化的方式封装)

​ 上一个blog,我们把get和post操作都写到了服务器代码中,显得很大很臃肿,应该进行封装。

​ 所以我们创建模块,把所有的路由操作封装在这个文件里面:

image-20200516152430741

model.js,即把路由以属性的方式封装到app对象,最后导出这个app对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
var ejs=require('ejs');

var fs=require('fs');

var app={
//login 路由
login:function(req,res){
console.log('login');
//res.end('login');

//渲染到views/form.ejs
ejs.renderFile('views/form.ejs',{},function(err,data){
res.end(data);

})
},
dologin:function(req,res){

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

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

//res.end(postStr);
console.log(postStr);

fs.appendFile('login.txt', postStr + '\n', function (err) {

if (err) {
console.log(err);
return;
}
console.log('写入数据成功');
})

res.end("<script>alert('登录成功');history.back();</script>")
})
},
register:function(req,res){

console.log('register');

res.end('register');
},
home:function(req,res){

console.log('home');

res.end('home');
}
}

//app.login('req');
//
//app['login']('req');

module.exports=app;

服务器代码就使用这个路由文件model.js,根据获取的URL的pathname确定使用那个路由,从而执行特点的方法。

这样代码大大简化了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var http=require('http');

var url=require('url');

var model=require('./model/model.js');


//model['login']('111','22');//调用model中的路由,根据名称执行不同操作

////
http.createServer(function(req,res){

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

var pathname=url.parse(req.url).pathname.replace('/','');
//因为url.parse(req.url).pathname是带/的,我们需要和model无斜杠的使用匹配起来

if(pathname!='favicon.ico') {
//如果跳转到别的没定义的页面,则让它回到home页面
try {
model[pathname](req, res);
} catch (err) {
model['home'](req, res);
}

}




}).listen(8001);

​ 这样我们实现了路由模块的封装,如果在model.js一些路由的方法比较复杂,其实还能进一步封装。此外,我们还能以别的方式进行封装。

封装仿照express框架封装的路由

​ Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

使用 Express 可以快速地搭建一个完整功能的网站。

​ Express 框架核心特性:

​ 可以设置中间件来响应 HTTP 请求。

​ 定义了路由表用于执行不同的 HTTP 请求动作。

​ 可以通过向模板传递参数来动态渲染 HTML 页面。

​ (后续我们会接着学)

​ 现在,我们想封装一个这样的路由:直接能够暴露一个接口,然后能够面向这个接口,根据请求方法进行路由匹配及相关处理:

image-20200516153741691

​ (这样封装使用的形式更简洁)

测试结果:

image-20200516153430322

image-20200516153458249

​ 现在,我们先介绍一下一些基本JS知识:

​ 知识1:对象属性和方法的定义:

image-20200516153942692

​ 知识2:注册方法与使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);

​ 运行结果:image-20200516155303439

​ 流程理解:首先,定义了一个G对象和带两个参数的app方法;之后定义了一个app的get方法(也是带两个参数)。当执行app.get时,相当于传入了两个参数(‘login’和具体的callback),此时相当于注册了G[login]的一个方法(为具体的callback)。当最后定时1s异步执行app方法时,传参(’req’,’res’),进入app代码,判断是否存在G[‘login’],如果存在则执行注册的方法 G[‘login’] (req,res),最后输出结果。

​ 现在,我们回归到http服务器中。

​ 最早,我们创建一个http服务器时:

image-20200516155621961

里面的方法定义,其实就是可以看做之前我们定义的app,所以又可以写成:

image-20200516155702845

​ 相当于,一直监听这个app,只有有请求就会出发app这个方法。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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);
// http.createServer(function(req,res){

// }).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,只要有请求,就会触发这个请求对应的app这个方法。

  现在我们更全面(例如除了get方法,还有post方法)地把这些路由方法封装成一个文件,只暴露接口给HTTP服务器使用。

封装路由文件(类似express框架的风格):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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的值,赋值给body*/


//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();

​ 服务器代码(使用EJS模板渲染):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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){
//使用定义的send
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('新闻数据');
})

执行结果:

login:

image-20200516164915369

image-20200516164929879

image-20200516164935115

news:

image-20200516164838558

register:

image-20200516164758015

小结:这样就实现了express框架封装的路由,逻辑清晰,使用起来很方便。