Node进阶第五天
为什么mysql不用开mongod –dbpath xx…
答:因为mysql会在”服务”中运行,也就是开机时自动启动并且长久驻扎在内存中了。
mongodb其实也能通过设置来设成windows中的服务。
案例:
01每次GET /的时候插入一条数据。
(具体api可以查看mongodb的文档)
const MongoClient = require('mongodb').MongoClient;
var express = require('express');
var app = express();
app.listen(3000);
app.get("/", (req, res) => {
const url = 'mongodb://localhost:27017/';
const dbName = 'myproject';
MongoClient.connect(url,{useNewUrlParser:true}, function (err, client) {
//回调函数表示连接成功做的事情
if(err){
console.log("数据库连接失败");
return;
}
console.log("数据库连接成功");
const db = client.db(dbName);
//插入数据,集合如果不存在,也没有关系,程序会帮你创建
db.collection('student').insertOne({
"name":"哈哈",
"age": parseInt(Math.random() *100 + 10)
},function(err,result){
if(err){
console.log(err);
return;
}
//插入之后做的事情,result表示插入结果。
console.log(result);
res.send(result);
client.close();
});
});
});
02把常用的增删改查,都封装成为module
DAO data access object 数据访问对象
是一个数据访问接口。
使用我们自己写的模块来完成数据库的操作。
一、 数据库
想想我们的百度百家Ajax案例,当时调用了百度的JSON,有一个参数叫做page=3,生成的JSON,
这个就是分页,就是我们像寻找所有的新闻,但是是位于第三页的新闻。那么有两种做法:
1) 错误的做法:就是将所有的result都读取到数组,然后进行数据操作,进行分页;
2) 正确的做法:就是在数据库中,只读取这么多内容。
错误的,我们试图每次都读取全部数据,但是这样开销很大
db.find(“student”,{},function(err,result){
for(var i = 10*page;i<10*(page+1);i++)
{
a.push(result[i]);
}
res.send(a);
});
所以,mongodb提供了傻傻的两个函数,极傻无比。
limit()、skip()
db.student.find().limit(4).skip(4);
limit表示读取的条数,skip表示略过的条数,limit和skip配合使用就是分页查询。
加入,第一页是page=0。每页10条,所以当前页的查询语句
db.student.find({}).limit(10).skip(page*10)
能分页的个数
db.student.stats().count;
案例源代码:
db.js:
//这个模块封装了所有对数据库的常用操作。
const MongoClient = require('mongodb').MongoClient;
const settings = require('../settings');
//不管数据库什么操作,都是先连接数据库,所以我们可以把连接数据库
//封装成内部函数
function __connectDB(callback) {
var url = settings.dburl; //从settings文件中读数据库地址
MongoClient.connect(url, function (err, client) {
const db = client.db('huha');
//这里是新增加的
if (err) {
callback(err, db);
return;
}
callback(err, db, client);
});
};
//插入数据
exports.insertOne = function (collectionName, json, callback) {
__connectDB(function (err, db, client) {
if (err) {
callback(err, null);
return;
}
db.collection(collectionName).insertOne(json, function (err, result) {
callback(err, result);
client.close();//关闭数据库
});
})
};
//查找数据,找到所有数据
exports.find = function (collectionName, json, C, D) {
var result = [];//结果数组
if (arguments.length == 3) {
console.log("有三个参数");
//那么参数C就是callback,参数D没有传。
var callback = C;
var skipnumber = 0;
//数目限制
var limit = 0;
} else if (arguments.length == 4) {
console.log("有四个参数");
var callback = D;
var args = C;
//应该省略的条数
var skipnumber = args.pageamount * args.page;
//数目限制
var limit = args.pageamount;
} else {
throw new Error("find函数的参数个数,必须是3个,或者4个");
}
//连接数据库,连接之后查找所有
__connectDB(function (err, db, client) {
var cursor = db.collection(collectionName).find(json).skip(skipnumber).limit(limit);
cursor.each(function (err, doc) {
if (err) {
callback(err, null);
return;
}
if (doc != null) {
result.push(doc); //放入结果数组
} else {
//遍历结束,没有更多的文档了
callback(null, result);
}
});
client.close();
});
}
//删除
exports.deleteMany = function (collectionName, json, callback) {
__connectDB(function (err, db, client) {
if (err) {
callback(err, null);
return;
}
//删除
db.collection(collectionName).deleteMany(
json,
function (err, results) {
console.log(results);
client.close();
callback();
}
);
});
}
//修改
exports.updateMany = function (collectionName, json1, json2, callback) {
__connectDB(function (err, db, client) {
db.collection(collectionName).updateMany(
json1,
json2,
function (err, results) {
callback(err, results);
});
});
}
settings.js:
module.exports = {
"dburl": 'mongodb://localhost:27017'
}
02.js:
var express = require('express');
var app = express();
var db = require('./model/db.js')
//增加
app.get('/', function (req, res) {
db.insertOne("student", { "name": "小红", "age": 18 }, function (err, result) {
if (err) {
console.log("插入失败");
return;
}
res.send("插入成功");
});
});
//查询
app.get('/du', function (req, res) {
//这个页面现在接收一个page参数,
var page = parseInt(req.query.page); //express中读取get参数很简单
//查找4个参数,在哪个集合查,查什么,分页设置,查完之后做什么
db.find("student", {}, function (err, result) {
if (err) {
console.log(err);
}
res.send(result);
});
});
//删除
app.get('/shan', function (req, res) {
var name = parseInt(req.query.name);
db.deleteMany("teacher", { "name": "小红" }, function (err, result) {
res.send(result);
});
});
//修改
app.get("/xiugai", function (req, res) {
db.updateMany("student", { "name": "小红" }, { $set: { "name": "呼哈" } }, function (err, result) {
if (err) {
console.log(err);
}
res.send(result);
});
});
app.listen(3000);
项目—小小留言本
做一个留言本,能够增删改:
ejs页面中,如果我们想使用underscore的模板,就会有模板冲突的问题。
underscore用的:
<script type="text/template" id="moban">
<div class="list-group">
<a href="#" class="list-group-item active">
<h4 class="list-group-item-heading"><%=xingming%>></h4>
<p class="list-group-item-text"><%=liuyan%></p>
</a>
</div>
</script>
ejs以为是自己的模板。所以报错,提示你没有传递姓名参数。
解决方法就是underscore源码。在源码中搜索<% 修改为{{即可。
项目整体目录结构:
03.js:
var express = require('express');
var app = express();
var db = require('./model/db.js');
var formidable = require('formidable');
var ObjectId = require('mongodb').ObjectId;
//设置模板引擎
app.set("view engine", "ejs");
//静态服务
app.use(express.static("./public"));
//显示留言列表
app.get("/", function (req, res, next) {
db.getAllCount("liuyanben", function (count) {
res.render('index', {
"pageamount": Math.ceil(count / 4)
});
});
});
//读取所有留言,这个页面是供ajax使用的
app.get("/du", function (req, res, next) {
//可以接受一个参数
var page = parseInt(req.query.page) || "0";
db.find("liuyanben", {}, { "sort": { "shijian": -1 }, "pageamount": 4, "page": page }, function (err, result) {
res.json({ "result": result });
});
});
//处理留言
app.post("/tijiao", function (req, res, next) {
console.log("对这里请求了");
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields) {
//写入数据库
db.insertOne("liuyanben", {
"xingming": fields.xingming,
"liuyan": fields.liuyan,
"shijian": new Date()
}, function (err, result) {
if (err) {
res.send({ "result": -1 }); //-1是给ajax看的
return;
}
res.send({ "result": 1 });
});
console.log("收到请求了" + fields.xingming + fields.liuyan);
});
});
//得到总数量
app.get("/count", function (req, res) {
});
//删除
app.get('/shanchu', function (req, res, next) {
//得到参数
var id = req.query.id;
db.deleteMany("liuyanben", { "_id": ObjectId(id) }, function (err, result) {
res.redirect("/");
});
});
app.listen(3000);
settings.js:
module.exports = {
"dburl": 'mongodb://localhost:27017'
}
index.ejs:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="css/bootstrap.min.css">
<style>
.row h4 {
text-align: center;
}
#chenggong,
#shibai {
display: none;
}
.liuyankuai {
padding: 10px 0;
border-bottom: 1px solid #ccc;
}
</style>
<title>Hello, world!</title>
</head>
<body>
<h1>我的留言本</h1>
<div class="container">
<div class="row">
<form class="form-horizontal col-lg-6">
<div class="form-group">
<label for="xingming" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="xingming" placeholder="姓名">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">留言</label>
<div class="col-sm-10">
<textarea style="resize:none;" name="liuyan" id="liuyan" class="form-control" rows="3"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" id="tijiao" class="btn btn-success">提交</button>
</div>
</div>
<div id="chenggong" class="alert alert-success" role="alert">
<a href="#" class="alert-link">表单已经成功提交</a>
</div>
<div class="alert alert-danger" id="shibai" role="alert">
<a href="#" class="alert-link">表单提交失败</a>
</div>
</form>
</div>
<nav aria-label="Page navigation">
<ul class="pagination">
<% for(var i =1;i<=pageamount; i++){ %>
<li data-page="<%=i%>" class="yemaanniu">
<a href="#">
<%=i%>
</a>
</li>
<% } %>
</ul>
</nav>
<div id="quanbuliuyan">
</div>
</div>
<script type="text/template" id="moban">
<div class="liuyankuai">
<p>[_id]{{=id}}</p>
<p>[姓名]{{=xingming}}</p>
<p>[留言]{{=liuyan}}</p>
<p>[时间]{{=shijian}}</p>
<p><a href="/shanchu?id={{=id}}" class="shanchu" >删除</a></p>
</div>
</script>
<script src="js/underscore.js"></script>
<script src="js/jquery.min.js"></script>
<script>
var nowpage = 1;
//删除
//给第一个页面,补一个active
$(".pagination").find("li").eq(0).addClass("active");
//给所有的页码按钮,添加监听
$(".yemaanniu").click(function () {
nowpage = parseInt($(this).attr("data-page"));
//重新发起请求即可
getData(nowpage);
$(this).addClass("active").siblings().removeClass("active");
});
$("#nextbtn").click(function () {
nowpage++;
if (nowpage)
getData(nowpage);
});
//默认请求第一页数据
getData(1);
//Ajax请求数据
function getData(page) {
$.get("/du?page=" + (page - 1), function (result) {
//这里接收时result,但是这个json里面有一个key叫做result。
//得到模板,弄成模板函数
var compiled = _.template($("#moban").html());
//清空全部留言中的所有节点
$("#quanbuliuyan").html("");
for (var i = 0; i < result.result.length; i++) {
//数据绑定
var html = compiled({
liuyan: result.result[i].liuyan,
xingming: result.result[i].xingming,
shijian: result.result[i].shijian,
id: result.result[i]._id,
});
//DOM操作,添加节点
$("#quanbuliuyan").append($(html));
}
});
}
//Ajax提交表单
$("#tijiao").click(function () {
$("#shibai").hide();
$("#chenggong").hide();
//Ajax提交表单
$.post("/tijiao", {
"xingming": $("#xingming").val(),
"liuyan": $("#liuyan").val()
}, function (result) {
if (result.result == -1) {
$("#shibai").fadeIn();
} else if (result.result == 1) {
//提交成功
$("#chenggong").fadeIn();
//数据库真的存储了,但是当前页面无法显示,这是因为需要刷新
//才能用ajax从/du中得到新的。所以我们先用一个假盒子凑出来
var compiled = _.template($("#moban").html());
var html = compiled(
{
liuyan: $("#liuyan").val(),
xingming: $("#xingming").val(),
shijian: new Date()
}
);
console.log($("#quanbuliuyan"));
$(html).insertBefore($("#quanbuliuyan"));
}
});
});
</script>
</body>
</html>
db.js:
//这个模块封装了所有对数据库的常用操作。
const MongoClient = require('mongodb').MongoClient;
const settings = require('../settings');
//不管数据库什么操作,都是先连接数据库,所以我们可以把连接数据库
//封装成内部函数
function __connectDB(callback) {
var url = settings.dburl; //从settings文件中读数据库地址
MongoClient.connect(url, function (err, client) {
const db = client.db('huha');
//这里是新增加的
if (err) {
callback(err, db);
return;
}
callback(err, db, client);
});
};
//插入数据
exports.insertOne = function (collectionName, json, callback) {
__connectDB(function (err, db, client) {
if (err) {
callback(err, null);
return;
}
db.collection(collectionName).insertOne(json, function (err, result) {
callback(err, result);
client.close();//关闭数据库
});
})
};
//查找数据,找到所有数据
exports.find = function (collectionName, json, C, D) {
var result = [];//结果数组
if (arguments.length == 3) {
// console.log("有三个参数");
//那么参数C就是callback,参数D没有传。
var callback = C;
var skipnumber = 0;
//数目限制
var limit = 0;
} else if (arguments.length == 4) {
// console.log("有四个参数");
var callback = D;
var args = C;
//应该省略的条数
var skipnumber = args.pageamount * args.page || 0;
//数目限制
var limit = args.pageamount || 0;
//排序方式
var sort = args.sort || {};
} else {
throw new Error("find函数的参数个数,必须是3个,或者4个");
}
//连接数据库,连接之后查找所有
__connectDB(function (err, db, client) {
var cursor = db.collection(collectionName).find(json).skip(skipnumber).limit(limit).sort(sort);
cursor.each(function (err, doc) {
if (err) {
callback(err, null);
return;
}
if (doc != null) {
result.push(doc); //放入结果数组
} else {
//遍历结束,没有更多的文档了
callback(null, result);
}
});
client.close();
});
}
//删除
exports.deleteMany = function (collectionName, json, callback) {
__connectDB(function (err, db, client) {
if (err) {
callback(err, null);
return;
}
//删除
db.collection(collectionName).deleteMany(
json,
function (err, results) {
console.log(results);
callback(null,results);
client.close();
}
);
});
}
//修改
exports.updateMany = function (collectionName, json1, json2, callback) {
__connectDB(function (err, db, client) {
db.collection(collectionName).updateMany(
json1,
json2,
function (err, results) {
callback(err, results);
//这里可能会有错误
client.close();
});
});
}
//得到文件总数
exports.getAllCount = function (collectionName, callback) {
__connectDB(function (err, db, client) {
var a = db.collection(collectionName).count({}).then(function (count) {
// console.log(count);
callback(count);
client.close();
});
});
}
二、Cookie和Session
HTTP是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到,这是同一个浏览器在访问同一个页面。每一次的访问,都是没有关系的。
那么世界就乱套了,比如说我上一次访问,登陆了,下一次访问,又让我登录,不存在登录着事儿了。
第一次访问一个服务器,不可能携带cookie。必须是服务器得到这次请求,在下行的响应报头中携带cookie信息,此后每一次浏览器往这个服务器发出的请求,都会携带这个cookie。
·cookie是不加密的,用户可以自由看到;
·用户可以删除cookie,或者禁用它
·cookie可以被篡改
·cookie可以用于攻击
·cookie存储量很小。未来实际上要被localStorage干掉。
express中的cookie,你肯定能想到。res负责设置cookie,req负责识别cookie。
Express中的Cookie
·Express中,使用cookie-parser中间件来设置cookie.
·主要设置选项:domain、path、maxAge、signed
1.js:
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.get('/', function (req, res) {
res.cookie('xihao', 'tfboys', { maxAge: 900000, httpOnly: true })
res.send();
});
app.listen(3000);
这个图是服务器发给浏览器的cookie,是明码:
再次请求的时候可以看到浏览器携带的cookie,是明码。
test.js:
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
//使用cookie必须要使用cookie-parser中间件
app.use(cookieParser());
app.get('/', function (req, res) {
//maxAge在Express中毫秒为单位
// res.cookie('xihao', 'tfboys', { maxAge: 900000, httpOnly: true })
res.send("猜你喜欢" + JSON.stringify(req.cookies));
});
app.get("/gonglue", function (req, res) {
//得到get请求,用户查询的目的地
var mudidi = req.query.mudidi;
//记录用户喜好
//先读取用户的喜好,然后把新的数据push进入数组,设置新的数据
var mudidiarray = req.cookies.mudidi || [];
mudidiarray.push(mudidi);
res.cookie("mudidi", mudidiarray, { maxAge: 900000, httpOnly: true });
res.send(mudidi + '旅游攻略');
});
app.listen(3000);
4.2 Session
Session不是天生就有的,是依赖于cookie。
cookie是明码传输的,
浏览器通过给服务器发送一段”乱码”cookie,让服务器进行对比,看看这个客户端是谁。
session依赖cookie,当一个浏览器禁用cookie的时候,登陆效果消失;或者用户清除了cookie,登录也消失。
session比cookie不一样在哪里呢? session下发的是乱码,并且服务器自己缓存一些东西,下次浏览器的请求带着乱码上来,此时与缓存进行比较,看看是谁。
所以,一个乱码,可以对应无限大的数据。
任何语言中,session的使用,是”机理透明”的,他是帮你设置cookie的,但是足够方便,让你感觉不到这事儿和cookie有关。
session中乱码和存储数据比对的这个库是存储在服务器的内存中的,一旦服务器关机了,就会消失了。
nodejs中的session:
express中使用express-session来使用session。
05.js:
var express = require('express');
var app = express();
var session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
}));
app.get('/', function (req, res) {
if (req.session.login == "1") {
res.send("欢迎你");
} else {
res.send('你没有登陆!!');
}
})
app.get('/login', function (req, res) {
req.session.login = "1"; //设置这个session
res.send('你已经成功登陆');
});
app.listen(3000);
看看这段被加密的cookie:
做一个利用session的登陆界面:
var express = require('express');
var app = express();
var db = require('./model/db.js');
var session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
}));
app.set('view engine', 'ejs');
app.get('/', function (req, res) {
if (req.session.login == "1") {
res.send("欢迎" + req.session.username);
} else {
res.send("没有成功登录");
}
});
app.get('/login', function (req, res) {
res.render('denglu');
});
app.get('/checklogin', function (req, res) {
var tianxiedeusername = req.query.username;
var tianxiepassword = req.query.password;
//根据用户填写的姓名,去数据库里面找这个文档,读出密码
//如果读取的密码,和填写的密码一样,那么登录成功了
//如果读取的密码和填写的密码不一样,登陆失败.
//如果根本没有找到这个记录,那么就说明用户名填写错了
db.find("users", { "username": tianxiedeusername }, function (err, result) {
console.log(result);
if (result.length == 0) {
res.send('你的登录名写错了,没有这个注册用户');
return;
}
var shujukuzhongdepassword = result[0].password;
if (shujukuzhongdepassword == tianxiepassword) {
req.session.login = "1";
req.session.username = result[0].username;
res.send('成功登录!你是' + result[0].username);
} else {
res.send('用户名对了密码错误');
}
});
});
app.listen(3000);
加密使用的是MD5加密。
加密’1’:
md5(1,32) = c4ca4238a0b923820dcc509a6f75849b
md5(1,16) = a0b923820dcc509a
加密’2’:
md5(2,32) = c81e728d9d4c2f636f067f89cc14862c
md5(2,16) = 9d4c2f636f067f89
不管你加密多大的东西,哪怕10M的文字,都会加密为32位的字符串,就是密码。并且神奇的,数学上能够保证,哪怕你更改一个文字,都能大变。
MD5是数学上,不能破解的。不能反向破解。
也就是说,c4ca4238a0b923820dcc509a6f75849b 没有一个函数,能够翻译成为1的。
但是,有的人做数据库就是把1~999999所有数字都用MD5加密了,然后进行了列表,所以有破解的可能。