开发者学堂课程【Node.js 入门与实战:课时69: 提取 router.js 模块01】学习笔记,与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/588/detail/8304
课时69: 提取 router.js 模块01
1、路由模块,负责路由判断,单独提取一个路由模块。
2、新建一个js文件,文件名叫router.js,用来保存路由。
//该模块负责封装所有路由判断代码
3、根据这三个步骤进行思考:
(1)思考,该模块中要封装什么代码?
封装以下代码:
//先根据用户请求的路径(路由) ,将对应的HTML页面显示出来
if (req. pathname
·===
'/'
·
|
|
·
req.pathname
·===
' /index'
·
&&
·
req.method
·===
' get')
·
{
// 1.读取data.json 文件中的数据,并将读取到的数据转换为list 数组
readNewsData(function(list) {
// 2.在服务器端使用模板引擎,将list 中的数据和 index. html文件中的内容结合渲染给客户端
res . render(path. join(__ dirname, 'views' ,' index.html'), { list: list });
});
} else if
·
(req.url
·
=== . ‘/submit'
·
&& req. Method
·===
' get') {
//读取 submit.html 并返回
res. render(path. join(__ dirname, 'views' ,' submit. html' ));
} else if
·
(req. pathname === ' /item' && req . method == ' get ' ) {
// 1.获取当前用户请求的新闻的id
// url0bj .query. id
// 2.读取data.json 文件中的数据,根据id找到对应新闻
readNewsData(function(list_ news) {
var model = null ;
//循环list_news中的数据,找到和id值相等的数据
for (var i - 0; i < list_ news.length; i++) {
//判断集合中是否有与用户提交的id相等的新闻
if (list_ news[i]. id. toString()
===
req. query.id) {
//如果找到了相等的新闻,则将其记录下来
model = list_ news[i];
break;
}
}
if. (model)·f
// 3.调用res. render() 函数进行模板引擎的渲染
res . render(path. join(__ dirname,' 'views' ,' details.html'),f item:model ]);
}
else
{
res .end('No Such Item');
}
});
}
else if (req .pathname
=== ’
. /add' && req . method
===‘
get')
{
// 1.读取data.json 文件的数据
readNewsData(function(list)
{
//2.
//在把新闻添加到list之前,为新闻增加一个id属性
req. query.id . list. length;
//向数组对象list中push一条新闻
list . push(req . query);
// 3.写入data.json 文件
writeNewsData(JSON. stringify(list), function()
{
//重定向
res . statusCode
=
302 ;
res . statusMessage
=
Found' ;
res. setHeader('Location', '/');
res. end();
}
);
}
);
}
else if-(req.url
===’
/add'
&& . req. method .
===
'post')
{
//1. 读取data. json
readNewsData(function(list)
{
//2. 读取用户 post提交的数据
postBodyData(req, function(postData)
{
//3.为用户提交的新闻增加一个id属性,并且把新闻对象push 到list中
postData.id
=
list. length;
list . push(postData);
// 4.将新的list数组,在写入到data.json 文件中
writeNewsData(JSON. stringify(list), function()
{
//重定向
res . statusCode
=
302 ;
res . statusMessage = " Found' ;
res. setHeader('Location' , '/');
res. end();
}
);
}
);
}
);
}
else if (req . url. startsWith( '/resources') && req. method
===
'get')
{
//如果用户请求是以/resources 开头,并且是get请求,就认为用户是要请求静态资源
//
/resources/images/s. gif
res . render(path. join(__ dirname, req. url));
}
else
{
res .writeHead(404, "Not Found',
{
‘
content-
t
ype
’
:
‘
text/html;
charset
=
utf-
8’
}
);
res .end( '404, Page Not Found.')
;
}
(2)思考,这些代码有用到外部的数据吗?如果用到了,是否需要通过参数将这些数据传递到当前模块中?
有用到req、res,把当前router.js暴露给一个函数,通过这个函数可以把它们传过来。
Module
.exports=function(req,res){
//
body
·····
};
既然现在router.js对外暴露给函数,在主模块中加载输入var
router
=
require
(‘./
router
.js‘);
加载完毕之后,Module
.exports=function(req,res){
};
返回的是一个函数,主模块中router也是一个函数,接着输入
//调用路由模块的返回值(函数),并将req和res对象传递给router.js模块
Router
(
req
,
res
);
这样通过路由可以把req和res传递给exports模块。将封装的代码剪切粘贴到//body·····位置,就可以把主模块包含的代码删掉,目前在主模块里面调用了context(req,res)和router(req,res)两个函数。
梳理以下代码:
if (req. pathname
·===
'/'
·
|
|
·
req.pathname
·===
' /index'
·
&&
·
req.method
·===
' get')
·
{
readNewsData(function(list) {
首先做了路由判断,路由判断里面有业务代码,readNewsData这个函数是在index.js里面被封装的,为了程序暂时可以运行,将函数拷贝到router.js,下面封装的函数在index.js里面没有用,因为这些函数是在处理业务时采用,index.js里面没有业务可以处理,所以这些函数就用不到,拷贝到rouder.js。
//封装一个读取data.json 文件的函数
function , readNewsData(callback){
fs. readFile(path. join(__ dirname,·'data' , ‘data. json'),' 'utf8', · function(err, data){
if. (err-&& err. code . !==. " ENOENT'){
throw- err;
}
var . list . =. JSON parse(data | |·'[]');
//通过调用回掉函数callback()将读取到的数据list,传递出去. callback(list);
});
}
//封装一个写入data.json 文件的函数
function writeNewsData(data, callback) {
fs .writeFile(path. join(_ dirname,’data ' , data. json' ), data, function(err) {
if (err) {
throw err;
}
//调用callback() 来执行当写入数据完毕后的操作
Callback();
}};
}
//封装一个获取用户post提成的数据的方法
Function
postbodydata
(
req
,
callback
){
Var array =[];
Req.on(‘data’,function(chunk){
Array.push(chunk);
});
//监听request 对象的end事件
//当end事件被触发的时候,表示上所有数据都已经提交完毕了
Req
.on(‘
end’
,
function
(){
var postBody =Buffer . concat( array);
//把获取到的buffer对象转换为一个字符串
postBody = postBody . toString( 'utf8' );
//把post请求的查询字符串,转换为一个json对象
postBody = querystring. parse (postBody);
//把用户post提交过来的数据传递出去
Callback
(
postbody
);
});
}
回到index.js,可以发现主模块使用服务加载var router=require(‘./router.js‘)和var context=require(‘./context.js‘),fs模块、path模块、mime模块等都可以删掉。在这里只需要加载http模块,创建服务,启动服务,在用户的req事件里面先调用context模块对req,res进行扩展,再调用router模块进行目标判断,现在的index.js的代码如下:
//1. 加载http模块
V
ar ,http· =". require( 'http');
var , context .= require(' ./context.js' );
var , router . =. require(' ./router . js');
// 2.创建服务
http. createServer(function(req, ·res) f
//调用context.js模块的返回值(函数),并将req和res对象传递给context.js模块
context(req, res);
//调用路由模块的返回值(函数),并将req和res对象传递给router.js 模块
router(req, res);
})listen(9090, function() {
console . log( 'http://localhost:9090');
});
(3)当前模块对外需要暴露的东西(module.exports的值)
4、在router.js里面,格式化一下,首先路由判断req,if(req. Pathname===‘/’||req. Pathname ===‘
index’
&&req . method
===get' ){
调用 rednewsdata 封装,调用 res.render,注意正式在 context 模块中为res增加了一个render函数,所以render函数无论在任何一个模块中只要能拿到res对象就可以用它。
5、//封装一个写入data.json 文件的函数
function writeNewsData(data, callback) {
fs .writeFile(path. join(_ dirname,’data ' , data. json' ), data, function(err) {
if (err) {
throw err;
}
//调用callback() 来执行当写入数据完毕后的操作
Callback();
}};
}
//把post请求的查询字符串,转换为一个json对象
postBody = querystring. parse (postBody);
这里fs模块和path模块,querystring模块需要加载。
var fs
=
require('fs');
var path
=
require(' path');
var querystring
=
require( ‘querystring ’
);
这样在主模块中启动服务,调用context测试过没问题,再调用路由模块,路由模块中做了路由判断并做了业务处理,现在测试一下是否有问题,启动没问题,列表可以显示,详情可以显示,新闻可以添加,证明现在封装路由模块提取的第一步没有问题,但是路由模块里面,它不光负责路由判断,还做很多业务处理,业务处理代码不应该放到路由模块里,路由模块职责非常单一,就是做路由判断,业务处理代码要放到业务模块里,接下来再提取业务模块。