nodejs--express开发个人博客(2)
上一部分已经实现了视图的雏形,现在加上逻辑操作。
登陆、注册、文章发表都需要用到数据库的数据存取,用的比较多的就是mongodb了。
MongoDB 是一个对象数据库,它没有表、行等概念,也没有固定的模式和结构,所有的数据以文档的形式存储。所谓文档就是一个关联数组式的对象,它的内部由属性组成,一个属性对应的值可能是一个数、字符串、日期、数组,甚至是一个嵌套的文档。下面是一个 MongoDB 文档的示例:
{ "_id" : ObjectId( "4f7fe8432b4a1077a7c551e8" ),
"uid" : 2004,
"username" : "byvoid",
"net9" : { "nickname" : "BYVoid",
"surname" : "Kuo",
"givenname" : "Carbo",
"fullname" : "Carbo Kuo",
"emails" : [ "byvoid@byvoid.com", "byvoid.kcp@gmail.com" ],
"website" : "http://www.byvoid.com",
"address" : "Zijing 2#, Tsinghua University" }
}
看着眼熟吧?恩,和json的格式一样。
使用方法:
去官网下载mongodb,解压到D:\softdata\nodejs\mongodb,在mongodb文件夹下新建文件夹取名myblog用来存放我们的数据。然后新打开一个cmd窗口,cd到我 们的D:\softdata\nodejs\mongodb\bin目录下,输入mongod -dbpath "D:\softdata\nodejs\mongodb\blog"设置数据库路径并启动数据库,最小化窗口不要关闭(以后启动也这么启动,另外可以进到bin目录下通过start mongo命令来启动mongodb的控制台,方便查看自己的数据库中的内容,一些常见的命令有:db.users.find()、db.users.count()、db.users.remove()等,具体是干什么的,自己试过就知道了)。
接下来我们在node中连接mongodb,打开 package.json,在 dependencies 属性中添加一行代码:"mongodb":"*",然后npm install安装mongodb模块,接下来在blog文件夹下创建 settings.js 文件,用于保存数据库的连接信息:
module.exports = { cookieSecret: 'myblog', db: 'blog', host: 'localhost'};
好了,数据库准备好了,下面就来写数据库存放的数据模型了。
在blog下面新建一个文件夹models用于存放数据模型。
在models下面新建db.js用于配置数据库:
var settings = require('../settings'), Db = require('mongodb').Db, Connection = require('mongodb').Connection, Server = require('mongodb').Server; module.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_PORT, {}));
接下来安装connect-mongo模块用来存储会话信息到数据库,关于connect-mongo的说明在nodejs开发指南里面做了详细解释,不多说。在 package.json 的dependencies中添加一行代码:"connect-mongo":"*",npm install安装connect-mongo模块。
然后在app.js里面引入数据库的相关配置文件就行了,先在“var path = require('path');”后添加以下内容:
var MongoStore = require('connect-mongo')(express);//注意:后面有(express) var settings = require('./settings');
在“app.use(express.methodOverride());”后添加:
app.use(express.cookieParser()); app.use(express.session({ secret: settings.cookieSecret, store: new MongoStore({ db: settings.db }) }));
至此数据库的配置已经做好了,下面可以用了。
登录和注册都需要比对数据库中的用户信息。在models下新建一个user.js,里面封装了对用户的操作:
var mongodb = require('./db'); function User(user){ this.name = user.name; this.password = user.password;};module.exports = User; User.prototype.save = function save(callback) { var user = { name: this.name, password: this.password, }; mongodb.open(function(err, db){ if(err){ return callback(err); } db.collection('users', function(err, collection){ if(err){ mongodb.close(); return callback(err); } collection.ensureIndex('name',{ unique:true }); collection.insert(user,{safe: true}, function(err, user){ mongodb.close(); callback(err, user); }); });});}; User.get = function get(username, callback){ mongodb.open(function(err, db){ if(err){ return callback(err); } db.collection('users', function(err, collection){ if(err){ mongodb.close(); return callback(err); } collection.findOne({ name: username },function(err, doc){ mongodb.close(); if(doc){ var user = new User(doc); callback(err, user); } else { callback(err, null); } }); });});};
然后在index.js里面修改登录的处理过程,即app.get("/login")和app.post("/login"),修改如下,同时为了使用User,需要在index.js开始的地方引入user.js
app.get('/login', function(req, res){ res.render('login',{ title:'登录', user:req.session.user, success:req.flash('success').toString(), error:req.flash('error').toString() }); }); app.post('/login', function(req, res){ var md5 = crypto.createHash('md5'), password = md5.update(req.body.password).digest('base64'); User.get(req.body.username, function(err, user){ if(!user){ req.flash('error', '用户不存在'); return res.redirect('/login'); } if(user.password != password){ req.flash('error', '密码错误'); return res.redirect('/login'); } req.session.user = user; req.flash('success','登陆成功'); res.redirect('/'); });});
注意到req.flash了?恩,这个是用于显示错误信息的,需要用到flash模块,在命令行中输入npm install connect-flash来安装flash,然后在app.js中var settings后面加上
var flash = require('connect-flash');
在“app.set('view engine', 'ejs');”后添加:
app.use(flash());
这样就可以使用flash的通知功能了。
还注意到什么没?crypto!crypto 是 Node.js 的一个核心模块,功能是加密并生成各种散列,使用它之前首先要声明 var crypto = require('crypto')。代码中使用它计算了密码的散列值。
到这里,应该登录就写完了,试试?点击登录按钮没有任何提示?因为还没有加·····
为了显示信息,还需要引入视图助手,《node.js开发指南》中这样描述:
“为了实现不同登录状态下页面呈现不同内容的功能,我们需要创建动态视图助手,通过它我们才能在视图中访问会话中的用户数据。同时为了显示错误和成功的信息,也要在动态视图助手中增加响应的函数。 ”
打开 app.js,在http.createServer()前添加以下代码:
app.use(function(req,res,next){var err = req.flash('error'), success = req.flash('success'); res.locals.user = req.session.user; res.locals.error = err.length ? err : null; res.locals.success = success.length ? success : null;next();});
在header.ejs的最后添加以下代码:
<% if (locals.success) { %>
<div >
<%= locals.success %>
</div>
<% } %>
<% if (locals.error) { %>
<div>
<%= locals.error %>
</div>
<% } %>
好了,再试试登录?
因为这个时候的数据库是空的,所以用户名不存在。
那接下来就通过注册添加新用户吧?毕竟mongodb的用法还不是很熟悉。
修改index.js中的reg的get和post处理:
app.get('/reg', function(req,res){ res.render('reg',{ title:'注册', user:req.session.user, success:req.flash('success').toString(), error:req.flash('error').toString() }); }); app.post('/reg', function(req,res){ if(req.body['password-repeat'] != req.body['password']){ req.flash('error','两次输入的口令不一致'); return res.redirect('/reg'); } var md5 = crypto.createHash('md5'); var password = md5.update(req.body.password).digest('base64'); var newUser = new User({ name: req.body.username, password: password, }); User.get(newUser.name, function(err, user){ if(user){ err = '用户已存在'; } if(err){ req.flash('error', err); return res.redirect('/reg'); } newUser.save(function(err){ if(err){ req.flash('error',err); return res.redirect('/reg'); } req.session.user = newUser; req.flash('success','注册成功'); res.redirect('/'); }); });});
好了,重启app.js,可以注册了,注册之后登录看看?
接下来,完善左边的导航部分,在header.ejs的nav后面添加:
<span><a title="发表文章" href="/post">发表文章</a></span>
<span><a title="退出" href="/logout">退出</a></span>
并在views下面增加发表文章的视图post.ejs:
<%- include header %> <form method="post"> 标题:<br /> <input type="text" name="title" /><br /> 内容:<br /> <textarea id="editor_id" name="post" style="width:700px;height:300px;"></textarea><br /> <input type="submit" value="提交" class="btn"/> </form> <%- include footer %>
下面该写文章的模型了,文章对应的有评论,把评论也作为一个数据模型,直接上代码:
post.js
var mongodb = require('./db'); function Post(user,title,tags,post) { this.user = user; this.title = title; this.post = post; } module.exports = Post; Post.prototype.save = function(callback) { var date = new Date(); var time = { date: date, year : date.getFullYear(), month : date.getFullYear() + "-" + (date.getMonth()+1), day : date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate(), minute : date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() } var post = { user: this.user,//用户 time: time,//发表时间 title: this.title,//标题 pv:0,//访问量 post: this.post,//文章内容 comments:[]//评论 }; mongodb.open(function (err, db) { if (err) { return callback(err); } db.collection('posts', function (err, collection) { if (err) { mongodb.close(); return callback(err); } collection.insert(post, { safe: true }, function (err,post) { mongodb.close(); callback(err,post); }); }); }); }; Post.getAll = function(user, callback) {//获取一个人的所有文章 mongodb.open(function (err, db) { if (err) { return callback(err); } db.collection('posts', function(err, collection) { if (err) { mongodb.close(); return callback(err); } var query={}; if(user){//因为index.js中app.get('/')为Post.getAll(null, function(err, posts){}),所以要判断user query.user=user; } collection.find(query).sort({ time: -1 }).toArray(function (err, docs) { mongodb.close(); if (err) { callback(err, null); } callback(null, docs); }); }); }); }; Post.getTen = function(user, page, callback) {//获取十篇文章(这个可以先不管,这是加分页的时候要用的) mongodb.open(function (err, db) { if (err) { return callback(err); } db.collection('posts', function(err, collection) { if (err) { mongodb.close(); return callback(err); } var query = {}; if(user){ query.user = user; } collection.find(query,{skip:(page-1)*5, limit:5}).sort({ time: -1 }).toArray(function (err, docs) { mongodb.close(); if (err) { callback(err, null); } docs.forEach(function(doc){ doc.post = markdown.toHTML(doc.post); }); callback(null, docs); }); }); }); } Post.getOne = function(user, day, title, callback) {//获取一篇文章 mongodb.open(function (err, db) { if (err) { return callback(err); } db.collection('posts', function(err, collection) { if (err) { mongodb.close(); return callback(err); } collection.findOne({"user":user,"time.day":day,"title":title},function (err, doc) { mongodb.close(); if (err) { callback(err, null); } console.log(doc); doc.post = markdown.toHTML(doc.post); console.log(doc.comments); doc.comments.forEach(function(comment){ console.log(comment); comment.content = markdown.toHTML(comment.content); }); callback(null, doc); cosole.log(doc.comments); }); collection.update({"user":user,"time.day":day,"title":title},{$inc:{"pv":1}}); }); }); };
comment.js
var mongodb = require('./db'); function Comment(user, day, title, comment) { this.user = user; this.day = day; this.title = title; this.comment = comment; } module.exports = Comment; Comment.prototype.save = function(callback) { var user = this.user, day = this.day, title = this.title, comment = this.comment; mongodb.open(function (err, db) { if (err) { return callback(err); } db.collection('posts', function (err, collection) { if (err) { mongodb.close(); return callback(err); } collection.findAndModify({"user":user,"time.day":day,"title":title} , [['time',-1]] , {$push:{"comments":comment}} , {new: true} , function (err,comment) { mongodb.close(); callback(err,comment); }); }); }); }
然后在index.js中引入需要的文件,本例中的引用的文件如下:
发表文章的响应:
app.get('/post', function(req, res){ res.render('post',{ title:'Post', user:req.session.user, success:req.flash('success').toString(), error:req.flash('error').toString() }); }); app.post('/post', function(req, res){ var currentUser = req.session.user, post = new Post(currentUser.name, req.body.title, tags,req.body.post); post.save(function(err){ if(err){ req.flash('error', err); return res.redirect('/'); } req.flash('success', '发布成功!'); res.redirect('/'); }); });
好了,现在可以发表文章了。再做一个调整,主页默认显示所有文章,需要修改两个地方,一是index.ejs,还有一个是index.js中的app.get("/");
index.ejs:
<%- include header %> <% locals.posts.forEach(function(post, index){ %> <p><h2><a href="/<%= post.user %>/<%= post.time.day%>/<%= post.title %>"><%= post.title %></a></h2> <p class="info"> 作者:<a href="/<%= post.user %>"><%= post.user %></a> | 日期:<%= post.time.minute %> </p> <p><%- post.post %></p> <p class="info">阅读:<%= post.pv %> | 评论:<%= post.comments.length %></p> <% }) %> <%- include footer %>
index.js中app.get("/"):
app.get('/',function(req,res){ Post.getAll(null, function(err, posts){ if(err){ posts = []; } res.render('index',{ title:'Home', user: req.session.user, posts:posts, success:req.flash('success').toString() }); }); });
好了,现在你看到应该是这样的:
哦,好像忘记加评论视图了,改天再写,顺便再说说点击文章标题进入文章页面和点击作者进入该作者的所有文章。
我也还是新手,所以写的一般,本来是写笔记的,写着写着就有点像新手教程了,⊙﹏⊙b汗,欢迎指正。