Node.js项目总结—Express框架
项目要实现的功能: 使用Express实现一个简单的数据管理系统
1.项目构建
1.1 使用npm初始化项目
npm init提供了项目初始化的功能,也解决了多个包的管理问题。
终端命令:npm init
初始化后的json文件
{
"name": "chaldea",//项目名
"version": "1.1.10",//版本号
"description": "人理存续保障机构菲尼斯·迦勒底,简称人理保障机构迦勒底或迦勒底。",//项目描述信息
"main": "index.js",//入口文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {//git地址
"type": "git",
"url": "https://github.com/Sherlock-Homles?tab=repositories"
},
"keywords": [//关键字
"servant"
],
"author": "竹川夏目",//作者
"license": "ISC",//当前项目的协议
"dependencies": {//引入的第三方模块管理
"art-template": "^4.13.2",
"cookie-session": "^2.0.0-beta.3",
"express": "^4.16.4",
"express-art-template": "^1.0.1",
"formidable": "^1.2.1",
"jquery": "^3.3.1",
"moment": "^2.23.0",
"mysql": "^2.16.0"
}
}
注意:
package.json与package-lock.json文件如果后期开发过程中,需要项目迁移,我们只需要将package.json文件迁移即可,在新项目下执行
npm install,所有第三方包会自动安装。
1.2 第三方模块的加载
使用
npm install+模块名 进行安装
1.3 项目目录
node_modules:安装的第三方模块目录
public:公共文件目录,包括静态页面引用的css,js,images文件等,其中upload为上传文件的保存目录
views:静态页面(HTML)目录
http.js:服务器模块
route.js:路由模块
business.js:业务处理模块,相当于PHP中控制器(controller)模块
db.js:数据库处理模块,相当于PHP中的模型(model)模块
#####1.4 数据表设计
servant表,存放所有的展示数据
master表,存放登录账号信息
2.具体功能实现
2.1 整合所有接口后的服务器模块和路由模块
服务器模块:
http.js
//服务器模块
var express = require('express');
//引入文件操作模块
var fs = require('fs');
var app = express();
var route = require('./route');
//加载中间件
var cookieSession = require('cookie-session');
//托管静态文件
app.use(express.static('public'));
app.engine('html', require('express-art-template'));
//注册中间件---session设置
app.use(cookieSession({
name: 'user_key', //客户端cookie的名称
keys: ['sherlock'] //用于加密的关键字
}));
//使用导入的路由模块
app.use(route);
//设置监听接口
app.listen('2060', () => {
console.log('请打开浏览器访问 http://localhost:2060');
});
路由模块:route.js
//设置外置路由
var express = require('express');
//引入业务模块
var business = require('./business');
var router = express.Router();
//优化路由模块
router
.get('/', business.getall) //列表页
.get('/getservant', business.getone) //从者信息页面
.get('/editservant', business.upuser_get) //修改信息页面
.post('/editpost', business.upuser_post) //修改数据到数据库
.get('/creat', business.creat) //添加信息页面
.post('/saveservant', business.save_post) //添加数据到数据库
.get('/deleteservant', business.delete) //从数据库中删除数据
.get('/login', business.login) //登录页面
.post('/dologin', business.dologin) //登录验证数据库方法
.get('/register', business.register) //注册页面
.post('/doregister', business.doregister) //注册写入数据库
.get('/logout',business.logout);// 退出登录并清除session
module.exports = router;
2.2 登录功能
实现思想: 接收
html接收到的username和password,通过where(username=" "&&password=" ")在数据表中查询,查询到数据则判断登录成功,将登录信息保存到session中,未查询到则判断登录失败。
业务模块
business.js
//加载模板引擎
var template = require('art-template');
var querystring = require('querystring');
//图片上传模块
var formidable = require('formidable');
var fs = require('fs');
//连接数据库模块
var db = require('./db');
template.defaults.root = './';
module.exports = {
//加载登录页面
login: function(req, res) {
res.render('login.html');
},
//登录验证
dologin: function(req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
//获取用户提交的数据,判断用户名密码是否正确
//获取用户提交的数据,判断用户名密码是否正确
//判断字段要加引号,以防止数据类型不同
db.where('email= "' + fields.username + '" and password= "' + fields.password + '"').select_login(function(data) {
console.log(data);
//判断数据库中是否存在数据,存在则data为查询到的完整信息,不存在则为空。
if (data != "") {
//数据正确,写入session
req.session.user_data = data;
res.send("<script>alert('登录成功!');window.location.href='/'</script>")
} else {
//数据验证失败,跳转回归登录页面
res.send("<script>alert('登录失败,请检查用户名或密码,重新登录!');window.location.href='/login'</script>")
}
});
});
},
}
数据库模块
db.js
//数据库连接模块
var mysql = require('mysql');
var connection = mysql.createConnection({
host: '127.0.0.1',
user: 'root',
password: 'root',
port: '3306',
database: 'chaldea'
});
module.exports = {
that: this,
where: function(wh) {
this.whe = wh;
return this;
},
//自定义的select_login函数,判断数据库中是否存在用户信息
select_login: function(callback) {
console.log(this.tables);
if (this.whe == undefined) {
var sql = 'select * from master';
} else {
var sql = 'select * from master where ' + this.whe;
}
console.log(sql);
this.tables='';
//用完后重置where条件,以免后续操作的数据重复
this.whe = undefined;
connection.query(sql, function(error, results, fields) {
if (error) {
console.log(sql);
callback(error);
return;
}
callback(results);
});
}
}
2.3 注册用户
**实现思想:**将html页面接收到的email和password保存到数据库数据表中。
业务模块:
business.js模块引入代码在2.2中已经写入了,接下来的代码只有具体功能实现代码。
//加载注册页面
register: function(req, res) {
res.render('register.html')
},
//注册写入数据库
doregister: function(req, res) {
var data = '';
req.on('data', function(save) {
data += save;
})
//绑定post传输的数据,监听接收完成
req.on('end', function() {
//获取post传输的数据
var post_data = querystring.parse(data);
//调用数据模块保存从者信息
db.save_register(post_data, function(data) {
if (data != 0) {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('添加成功!');location.href='http://localhost:2060';</script>");
} else {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('添加失败!');window.history.back(-1);</script>");
}
})
})
},
数据库模块:
db.js模块引入代码在2.2中已经写入了,接下来的代码只有具体功能实现代码。
//自定义的save_register函数,将注册信息保存到数据库中
save_register: function(data,callback) {
//组装SQL语句:接收过来的为对象数据{name:'xx',rank:'xx'}
//循环遍历对象
var set = '';
var key = '';
for (k in data) {
set += "'" + data[k] + "',";
key += k + ",";
}
//去掉最后一个逗号
set = set.slice(0, set.length - 1);
key = key.slice(0, key.length - 1);
var sql = 'INSERT INTO master (' + key + ') VALUES (' + set + ');';
console.log(sql);
connection.query(sql, function(error, sql_data) {
console.log(sql_data);
callback(sql_data.insertId);
});
}
2.3.10 注册优化:如果数据库中存在相同email字段,则提示去登陆
**优化思想:**将要注册的邮箱数据作为条件去查询数据表,如果有相同记录,则不予许注册,提示跳转登录;如果没有相同记录,则允许注册。
业务模块:
business.js只需要在业务模块添加一部分查询判断代码即可
//注册写入数据库
doregister: function(req, res) {
var data = '';
req.on('data', function(save) {
data += save;
})
//绑定post传输的数据,监听接收完成
req.on('end', function() {
//获取post传输的数据
var post_data = querystring.parse(data);
//查询数据库中是否已经存在这个email
db.where('email= "' + post_data.email + '"').select_login(function(date) {
if (date != '') {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('电邮地址已存在,前去登录!');location.href='/login';</script>");
} else {
//调用数据模块保存从者信息
db.save_register(post_data, function(data) {
if (data != 0) {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('注册成功!');location.href='/login';</script>");
} else {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('注册失败!');window.history.back(-1);</script>");
}
})
}
});
})
},
#####2.4 退出登录
实现思想: 点击退出按钮是,销毁session,并回到登录页面,不需要对数据库进行操作。
业务模块:
buiness.js
//退出登录,并清除session
logout: function(req, res) {
req.session = null;
//清除cookie:要销毁session,只需将其设置为null。
console.log('退出成功!');
//跳转回登录页面
res.send("<script>alert('成功退出迦勒底!');window.location.href='/login'</script>");
}
2.5 展示主页
实现思路:
1.查询session中是否存在登录信息,不存在则提示登录并跳转到登录页
2.存在session信息,则展示首页
业务模块:
business.js
//获取全部数据
getall: function(req, res) {
//判断是否登录
//获取并判断session
if (req.session.user_data) {
console.log(req.session.user_data);
console.log(req.session.user_data[0].name);
//利用db模块导出的方法
db.select(function(data) {
//利用回调函数获取数据
res.render('servant.html', { data: data, username: req.session.user_data[0].name });
});
} else {
//没有session信息,跳转到登录页
res.send('<script>alert("请登录后查看!");window.location.href="/login";</script>')
}
},
数据库模块:
db.js·
//判断是否有条件传过来
where: function(wh) {
this.whe = wh;
return this;
},
//自定义的select函数
select: function(callback) {
if (this.whe == undefined) {
var sql = 'select * from servant';
} else {
var sql = 'select * from servant where ' + this.whe;
}
this.table='';
//用完后重置where条件,以免后续操作的数据重复
this.whe = undefined;
connection.query(sql, function(error, results, fields) {
if (error) {
callback(error);
return;
}
callback(results);
});
},
2.6 展示单个从者信息页
业务模块:
business.js
//获取单条数据
getone: function(req, res) {
db.where('id=' + req.query.id).select(function(data) {
res.render('./servant_check.html', { data: data });
});
},
数据库模块:
db.js与展示页面的select方法相同,通过db.where方法获取到要查询的id条件,然后查询单条数据。
2.7 向数据库中添加一条从者信息
**实现思路:**这里针对图片上传,有两种实现方法:
1.没有图片上传模块:则就是简单的接受
post数据并存入数据库2.有图片上传模块:因为
formidable图片上传模块可以接受表单传过来的所有数据,所以不必额外再写接收post数据的代码。
formidable在npm上的介绍:A Node.js module for parsing form data, especially file uploads.[Node.js模块,用于解析表单数据,尤其是文件上载。]
业务模块:
business.js1.没有图片上传模块时:
//加载添加页面
creat: function(req, res) {
res.render('servant_creat.html');
},
//添加数据到数据库
save_post: function(req, res) {
var data = '';
req.on('data', function(save) {
data += save;
})
//绑定post传输的数据,监听接收完成
req.on('end', function() {
//获取post传输的数据
var post_data = querystring.parse(data);
//调用数据模块保存从者信息
db.save(post_data, function(data) {
if (data != 0) {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('添加成功!');location.href='http://localhost:2060';</script>");
} else {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('添加失败!');window.history.back(-1);</script>");
}
})
})
},
2.有图片上传模块:
//加载添加页面
creat: function(req, res) {
res.render('servant_creat.html');
},
//添加数据到数据库
save_post: function(req, res) {
//图片上传
var form = new formidable.IncomingForm();
//解决 跨磁盘分区移动 或操作文件会有权限问题-->添加一个 form.uploadDir='tmp' 即可(写一个临时路径)
form.uploadDir = 'public';
form.parse(req, function(err, fields, files) {
console.log(fields);
var times = new Date().getTime();
//组装上传路径
var file_path = './public/upload/' + times + files.photo.name;
//将缓存文件移动到指定的public目录下
fs.rename(files.photo.path, file_path, (err) => {
if (!err) {
//因为设置静态资源时,已经将public文件夹,写入数据库,不要加public
fields.photo = './upload/' + times + files.photo.name;
console.log(fields);
//调用数据模块保存从者信息
db.save(fields, function(data) {
if (data != 0) {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('添加成功!');location.href='http://localhost:2060';</script>");
} else {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('添加失败!');window.history.back(-1);</script>");
}
})
} else {
console.log('文件上传失败!' + err);
}
});
});
},
数据库模块:
db.js
//自定义save方法,添加新数据到数据库
save: function(data, callback) {
//组装SQL语句:接收过来的为对象数据{name:'xx',rank:'xx'}
//循环遍历对象
var set = '';
var key = '';
for (k in data) {
set += "'" + data[k] + "',";
key += k + ",";
}
//去掉最后一个逗号
set = set.slice(0, set.length - 1);
key = key.slice(0, key.length - 1);
var sql = 'INSERT INTO servant (' + key + ') VALUES (' + set + ');';
console.log(sql);
connection.query(sql, function(error, sql_data) {
console.log(sql_data);
callback(sql_data.insertId);
});
},
2.8 在页面上更新一条从者信息
实现思路:
1.先从数据库查询出要修改的那条数据,并展示到页面上。展示同样用的是
db.select方法2.保存修改后的数据到数据库:这里与添加相同,同样是分为有文件上传和没有文件上传的情况,这里就不再赘述无上传的情况了,直接就用
formidable模块进行解析表单数据。
业务模块:
business.js
//获取要修改的数据
upuser_get: function(req, res) {
db.where('id=' + req.query.id).select(function(data) {
res.render('./servant_edit.html', { data: data });
});
},
//向数据库中保存要修改的数据
upuser_post: function(req, res) {
//图片上传
var form = new formidable.IncomingForm();
//解决 跨磁盘分区移动 或操作文件会有权限问题-->添加一个 form.uploadDir='tmp' 即可(写一个临时路径)
form.uploadDir = 'public';
form.parse(req, function(err, fields, files) {
console.log(fields);
var times = new Date().getTime();
//组装上传路径
var file_path = './public/upload/' + times + files.photo.name;
//将缓存文件移动到指定的public目录下
fs.rename(files.photo.path, file_path, (err) => {
if (!err) {
//因为设置静态资源时,已经将public文件夹,写入数据库,不要加public
fields.photo = './upload/' + times + files.photo.name;
console.log(fields);
db.where('id=' + req.query.id).update(fields, function(changedRows) {
//http服务器相应的要求必须是字符串
if (changedRows == 1) {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('修改成功!');location.href='http://localhost:2060';</script>");
} else {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('修改失败!');window.history.back(-1);</script>");
}
})
} else {
console.log('文件上传失败!' + err);
}
});
});
},
数据库模块:
db.js
//自定义update方法:更新数据
update: function(data, callback) {
if (this.where == undefined) {
callback("修改时不能没有where条件!");
} else {
//组装SQL语句:接收过来的为对象数据{name:'xx',rank:'xx'}
//循环遍历对象
var set = '';
for (k in data) { //k为键,data[k]为值
set += k + "='" + data[k] + "',";
}
//去掉最后一个逗号
set = set.slice(0, set.length - 1);
var sql = 'update servant set ' + set + ' where ' + this.whe;
//重置where条件
this.whe = undefined;
connection.query(sql, function(error, sql_data) {
console.log(sql_data);
callback(sql_data.changedRows);
});
}
},
2.9 删除一条数据
实现思路:接收
get传过来的id,使用db.delete()方法删除数据表中的数据。
业务模块:
business.js
//删除数据库中的信息
delete: function(req, res) {
req.on('data', function() {
//必须要有这一步,否则不进入end中
//req.on(data)指每次发送的数据;
//req.on(end)数据发送完成;
})
req.on('end', function() {
db.where('id=' + req.query.id).delete(function(data) {
if (data == '') {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('删除成功!');location.href='http://localhost:2060';</script>");
} else {
res.setHeader('Content-type', 'text/html;charset=utf-8');
res.end("<script>alert('删除失败!');window.history.back(-1);</script>");
}
});
})
},
数据库模块:
db.js
//自定义删除方法,删除数据库中的数据
delete: function(callback) {
var sql = 'DELETE FROM servant WHERE ' + this.whe;
console.log(sql);
//重置where条件
this.whe = undefined;
connection.query(sql, function(error, sql_data) {
console.log(error);
console.log(sql_data);
callback(sql_data.message);
});
},
3. 补充
#####3.1 Destroying a session[销毁session]
To destroy a session simply set it to
null:要销毁session,只需将其设置为
null:req.session = null
#####3.2 判断浏览器的cookie是否开启,需要用到浏览器Navigator对象
cookieEnabled 属性可返回一个布尔值,如果浏览器启用了
cookie,该属性值为 true。如果禁用了 cookie,则值为 false。使用方法:
document.write("是否启用 Cookie: " + navigator.cookieEnabled); //是否开启cookie:true结果 : 是否启用 Cookie: true
#####3.3 图片上传
图片上传模块
var formidable = require('formidable');代码实现
//向数据库中保存要修改的数据 upuser_post: function(req, res) { //图片上传 var form = new formidable.IncomingForm(); //解决 跨磁盘分区移动 或 操作文件 会有权限问题-->添加一个 form.uploadDir='public' 即可(写一个临时路径) form.uploadDir = 'public';//设置上传路径 form.parse(req, function(err, fields, files) { console.log(fields); var times = new Date().getTime(); //组装上传路径 var file_path = './public/upload/' + times + files.photo.name; //将缓存文件移动到指定的public目录下 fs.rename(files.photo.path, file_path, (err) => { if (!err) { //因为设置静态资源时,已经将public文件夹,写入数据库,不要加public fields.photo = './upload/' + times + files.photo.name; console.log(fields); db.where('id=' + req.query.id).update(fields, function(changedRows) { //http服务器相应的要求必须是字符串 if (changedRows == 1) { res.setHeader('Content-type', 'text/html;charset=utf-8'); res.end("<script>alert('修改成功!');location.href='http://localhost:2060';</script>"); } else { res.setHeader('Content-type', 'text/html;charset=utf-8'); res.end("<script>alert('修改失败!');window.history.back(-1);</script>"); } }) } else { console.log('文件上传失败!' + err); } }); }); }文件操作权限问题
解决 跨磁盘分区移动 或 操作文件 会有权限问题–>添加一个
form.uploadDir='public'即可(写一个临时路径)。