node.js 封装模块

人盡茶涼 提交于 2020-03-08 18:31:45

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'),
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!