上一部分已經實現了視圖的雛形,現在加上邏輯操作。
登陸、註冊、文章發表都需要用到數據庫的數據存取,用的比較多的就是mongodb了。
MongoDB 是一個對象數據庫,它沒有表、行等概念,也沒有固定的模式和結構,所有的數據以文檔的形式存儲。所謂文檔就是一個關聯數組式的對象,它的內部由屬性組成,一個屬性對應的值可能是一個數、字符串、日期、數組,甚至是一個嵌套的文檔。下面是一個 MongoDB 文檔的示例:
{ "_id" : ObjectId( "4f7fe8432b4a1077a7c551e8" ),
"uid" : 2004,
"username" : "byvoid",
"net9" : { "nickname" : "BYVoid",
"surname" : "Kuo",
"givenname" : "Carbo",
"fullname" : "Carbo Kuo",
"emails" : [ "[email protected]", "[email protected]" ],
"website" : "http://www.byvoid.com",
"address" : "Zijing 2#, Tsinghua University" }
}
看着眼熟吧?恩,和json的格式一樣。
使用方法:
去官網下載mongodb,解壓到D:\softdata\nodejs\mongodb,在mongodb文件夾下新建文件夾取名blog用來存放我們的數據。然後新打開一個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,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);
}
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);
}
callback(null, doc);
});
collection.update({"user":user,"time.day":day,"title":title},{$inc:{"pv":1}});
});
});
};
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,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汗,歡迎指正。