1. form.js
// 模塊1 服務模塊,負責啓動服務
// 模塊2 拓展模塊,負責拓展req和res對象,爲req和res增加以下更方便好用的API
// 模塊3 路由模塊,負責路由判斷
// 模塊4 業務模塊,負責處理具體路由的業務的代碼
// 模塊5 數據操作模塊,負責進行數據庫操作
// 模塊6 配置模塊,負責報錯各種項目中用到的配置信息
var http = require('http');
var context = require('./11_context.js');
var router = require('./11_router.js');
var config = require('./11_config.js');
console.log('form');
http.createServer(function(req, res) { // 外部代碼只在開始時執行依次,內部代碼每請求一次就執行一次
console.log('server');
context(req, res); // 調用context.js的返回值,並將req和res傳遞過去
router(req, res);
}).listen(config.port, function() {
console.log('http://localhost:' + config.port);
});
2. context.js
// 模塊2 拓展模塊,負責拓展req和res對象,爲req和res增加以下更方便好用的API
// 爲req增加一個query屬性,該屬性中保存用戶get請求提交過來的數據
// req.query
// 爲req增加一個pathname屬性
// req.pathname
// 爲res增加一個render函數
var url = require('url');
var mime = require('mime');
var fs = require('fs');
var _ = require('underscore');
console.log('context');
// 讓當前模塊對外暴露一個函數,通過這個函數將10_form.js裏的req和res傳遞到當前context.js這個模塊中
module.exports = function(req, res) {
var urlObj = url.parse(req.url.toLowerCase(), true);
req.query = urlObj.query;
req.method = req.method.toLowerCase();
req.pathname = urlObj.pathname; // req.pathname和req.url等價
res.render = function(filename, tplData) {
fs.readFile(filename, function(err, data) {
if(err) {
res.writeHead(404, 'Not Found', {'Content-Type': 'text/html;charset=utf-8'});
res.end();
return;
}
// 如果用戶傳遞了模板數據,表示要進行模板替換,加載underscore
if(tplData) {
var fn = _.template(data.toString('utf-8'));
data = fn(tplData);
}
res.setHeader('Content-Type', mime.getType(filename));
res.end(data);
});
}
}
3. router.js
// 模塊3 路由模塊,負責路由判斷
var handler = require('./11_handler.js');
console.log('router');
module.exports = function(req, res) {
if(req.url === '/' || req.url === '/form' && req.method === 'get') {
handler.form(req, res);
} else if(req.url.startsWith('/add') && req.method === 'get') {
handler.addGet(req, res);
} else if(req.url.startsWith('/add') && req.method === 'post') {
handler.postGet(req, res);
} else if(req.pathname === '/item' && req.method === 'get') {
handler.item(req, res);
} else {
handler.handlerErrors(req, res);
}
}
4. handler.js
// 模塊4 業務模塊,負責處理具體路由的業務的代碼
var fs = require('fs'); // require加載都是同步的
var path = require('path');
var querystrng = require('querystring');
var config = require('./11_config.js');
console.log('handler');
module.exports.form = function(req, res) {
readNewsData(function(list) {
res.render(path.join(__dirname, 'htmls', 'form.html'), {list: list});
})
}
module.exports.addGet = function(req, res) {
readNewsData(function(list) {
req.query.id = list.length; // 添加id
list.push(req.query); // 新數據寫入list數組
// 所有數據全部重新寫入data.json文件
writeNewsData(JSON.stringify(list), function() {
res.statusCode = 302; // 以3開頭的狀態碼錶示跳轉
res.statusMessage = 'Found';
res.setHeader('Location','/'); // 瀏覽器重定向,跳轉到localhost:9090
res.end();
})
})
}
module.exports.postGet = function(req, res) {
readNewsData(function(list) {
postBodyData(req, function(postBody) {
postBody.id = list.length; // 添加id
list.push(postBody);
writeNewsData(JSON.stringify(list), function() {
res.statusCode = 302; // 以3開頭的狀態碼錶示跳轉
res.statusMessage = 'Found';
res.setHeader('Location','/'); // 瀏覽器重定向,跳轉到localhost:9090
res.end();
})
})
})
}
module.exports.item = function(req, res) {
readNewsData(function(list) {
var model = null;
for(var i=0; i<list.length; i++) { // 循環,找到id相等的數據
if(list[i].id.toString() === req.query.id ) {
model = list[i];
break;
}
}
if(model) {
res.render(path.join(__dirname, 'htmls', 'details.html'), {item: model});
} else {
res.end('No such item');
}
})
}
module.exports.handlerErrors = function(req, res) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('404, not Found,客戶端錯誤!')
}
// 封裝一個讀取data.json文件的函數,函數異步執行只能通過回調函數返回
function readNewsData(callback) {
fs.readFile(config.dataPath, 'utf-8', function(err, data) { // utf-8返回字符串
if(err && err.code !== 'ENOENT') { // 報錯且文件存在
throw err;
}
var list = JSON.parse(data || '[]'); // 原有數據轉換成數組,防止get提交的數據被覆蓋
callback(list); // 通過調用回調含糊callback將讀取到的數據list傳遞出去
})
}
// 封裝的寫入data.json文件的代碼
function writeNewsData(list, callback) {
fs.writeFile(config.dataPath, list, function(err) {
if(err) {
throw err;
}
callback();
})
}
// 封裝獲取用戶post提交的數據
function postBodyData(req, callback) {
// post方法提交數據可能數據量較大,分多次提交,所以必須監聽request的data事件,request的end事件標誌提交結束
var arr = [];
req.on('data', function(chunk) {
arr.push(chunk); // chunk的數據類型是Buffer,瀏覽器提交過來的一部分數據
});
req.on('end', function() {
var postBody = Buffer.concat(arr); // 轉換成一個大的Buffer對象
postBody = postBody.toString('utf-8'); // Buffer對象轉換成一個字符串,name=Karry&age=21
postBody = querystrng.parse(postBody); // 轉換成json對象,{name: 'Karry', age: 21}
callback(postBody); // 把post提交的數據傳遞出去
})
}
5. config.js
var path = require('path');
module.exports = {
"port": 9091,
"dataPath": path.join(__dirname, 'data', 'data.json'),
}