基于ES6和原生nodejs来实现一个基于MVC的增删查改功能示例分享
自定义路由的解耦实现
首先分别处理不同方式的请求:
const http = require('http');
const url = require('url');
const querystring = require('querystring');
const router = require('../router');
// switch method
function handleMethod (req, data, callback) {
switch(req.method) {
case "GET":
var query = url.parse(req.url, true).query; // true json, false string here
callback(query);
break;
case "POST":
// too much data, close connection
if(data.length > 1e6) {
return req.connection.destroy();
}
data = Buffer.concat(data).toString();
var params = querystring.parse(data);
callback(params);
break;
case "DELETE":
var query = url.parse(req.url, true).query; // true json, false string here
callback(query);
break;
// other method here to be handled // todo
}
}
// 处理http请求流
function handler(req,callback) {
var data = [];
req.on("error", function(err) {
return console.error(err);
}).on("data", function(chunk) {
data.push(chunk);
}).on('end', function() {
handleMethod(req,data,callback);
});
}
// 服务器开始
const start = function () {
var server = http.createServer((req, res)=>{
handler(req, (params)=>{
router(req, res, params);
});
});
server.listen('3000','127.0.0.1', ()=>{
console.log('server is running on port 3000');
});
}
module.exports.start = start;
路由列表模块:
const homeCtrl = require('../controller/home');
const editCtrl = require('../controller/edit');
const addCtrl = require('../controller/add');
// 定义路由列表
class List {
// 首页的处理
'/' (res) {
homeCtrl.render(res);
}
// 首页删除功能的处理
'/remove' (res, pathname) {
homeCtrl.remove(res, pathname);
}
// 编辑的处理 如: /edit/1
'/edit' (res, pathname, params, method) {
if(method === 'GET') {
homeCtrl.edit(res, pathname, params);
}else if(method === 'POST') {
editCtrl.edit(res, pathname, params);
}
}
// 添加的处理
'/add' (res, pathname, params, method) {
if(method === 'GET') {
homeCtrl.add(res, pathname, params);
}else if(method === 'POST') {
addCtrl.add(res, pathname, params);
}
}
// 搜索的处理
'/search' (res, pathname, params) {
homeCtrl.search(res, pathname, params);
}
}
// 获取自身全部路由列表
const routerAttrList = Object.getOwnPropertyNames(List.prototype);
// 对路由的判断
function isRouter(pathname) {
return routerAttrList.find((item, index)=>{
// 如果匹配,把当前定义的路由返回
if(pathname === item || pathname.startsWith(item) && (item !== '/')) {
return item;
}
});
}
module.exports = {
isRouter,
routerAttrList,
list: new List()
};
路由判断模块:
const fs = require('fs');
const url = require('url');
const routerList = require('./list');
const staticServer = require('../server/static'); // 静态文件服务器
const router = function (req, res, params) {
let pathname = url.parse(req.url).pathname;
// 关于静态文件服务器的判断
if(pathname.startsWith('/assets/')) {
return staticServer(res, pathname);
}
// 得到是否存在定义的路由
let routerItem = routerList.isRouter(pathname);
// 如果定义了该路由,那么执行定义路由的回调函数
if(routerItem) {
return routerList.list[routerItem](res, pathname, params, req.method);
}
// 没定义路由,那么返回404页面
res.writeHead(404, { 'Content-Type': 'text/html' });
fs.createReadStream(__dirname + '/../views/404.html', 'utf8').pipe(res);
};
module.exports = router;
其中路由支持所有的类型比如: GET、POST、PUT、DELETE等功能,如果后期需要自己添加就好,还有支持各种查询参数的比如:/edit/1
, /xxx?id=1&name=Joh
, 以及post的数据
静态文件服务器的实现
// 处理静态文件服务器
const fs = require('fs');
const path = require('path');
const mime = require('mime');
const staticServer = function (res, pathname) {
fs.readFile(__dirname + '/..' + pathname, 'utf8', function (err, data) {
if (err) {
return res.end(err.message);
}
// 读取文件,解析json,然后根据对应的扩展名,找到对应的mime Content-Type
let mimeType = mime.getType(pathname);
// 处理 text
if (mimeType.startsWith('text/')) {
mimeType += '; charset=utf-8';
}
res.writeHead(200, {
'Content-Type': mimeType
});
res.end(data);
});
}
module.exports = staticServer;
model中对实体对象的增删查改的方法
let musicList = require('./data');
class Music {
constructor(id, name, singer, isHightRate) {
this.id = id;
this.name = name;
this.singer = singer;
this.isHightRate = isHightRate;
}
// 获取所有音乐
getAllMusic() {
return musicList;
}
// 添加一首音乐
addMusic(name, singer, isHightRate) {
let id = musicList[musicList.length - 1].id - 0 + 1;
let json = {
id,
name,
singer,
isHightRate
};
var flag = false;
try{
musicList.push(json);
flag = true;
} catch(e){
console.log('add error');
}
return flag;
}
// 编辑一首音乐
editMusicById(id, name, singer, isHightRate) {
// 根据id查找数组中的索引
let index = musicList.findIndex(m => m.id === id);
if(index === -1) {
return false;
}
musicList[index].name = name;
musicList[index].singer = singer;
musicList[index].isHightRate = isHightRate === '1';
return true;
}
// 通过id删除音乐
removeMusicById(id) {
let index = musicList.findIndex(m => m.id === id);
if(index === -1) {
return false;
}
musicList.splice(index, 1);
return true;
}
// 通过id查询音乐
getMusicById(id) {
return musicList.find((item) => {
return item.id === id
});
}
}
module.exports = new Music();
在控制器中实现model和view的通信
举例post形式的添加功能
const music = require('../model/music');
class Add {
add(res, pathname, params) {
let name = params.name;
let singer = params.singer;
let isHightRate = params.isHightRate;
isHightRate = !!isHightRate;
var flag = music.addMusic(name, singer, isHightRate);
res.writeHead(302, {
'Location': flag ? '/' : '/add'
});
res.end();
}
}
module.exports = new Add();
其他说明
示例中关于功能中还需要使用第三方的库比如:node-mime 是用于处理mime类型的,在静态文件服务器中,根据类型来自动适配content-type的
插件地址:node-mine@github
node 环境
node -v
v9.9.0