https://github.com/haishangfeie/weibojavascript
express -e microblog
按提示輸入php
PS E:\code\nodejsExercise\express\3> cd .\microblog\ PS E:\code\nodejsExercise\express\3\microblog> npm i
如今,咱們先啓動網站看看css
npm start
若是能運行到以上的效果,那麼項目已經建立好了。html
那麼在正式開始建立網站前,我也試着對接下來的項目進行一個功能分析。
本項目是一個微博項目的簡單實現,須要包括以下功能:用戶的登陸、註冊、退出登陸,另外還有信息登陸功能。
大體規劃:java
能夠先寫一個index.html頁面看看效果:
node
接着將它改成模板:
在views文件夾新建一個header.ejsjquery
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title><%= title %> - Microblog</title> <link rel='stylesheet' href='/stylesheets/bootstrap.css' /> <style type="text/css"> body { padding-top: 60px; padding-bottom: 40px; } </style> <link href="stylesheets/bootstrap-responsive.css" rel="stylesheet"> </head> <body> <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a> <a class="brand" href="/">Microblog</a> <div class="nav-collapse"> <ul class="nav"> <li class="active"><a href="/">首頁</a></li> <li><a href="/login">登入</a></li> <li><a href="/reg">註冊</a></li> </ul> </div> </div> </div> </div>
在views文件夾新建一個footer.ejsgit
<hr /> <footer> <p><a href="http://www.byvoid.com/" target="_blank">BYVoid</a> 2012</p> </footer> </div> </body> <script src="/javascripts/jquery.js"></script> <script src="/javascripts/bootstrap.js"></script> </html>
在views文件夾,修改index.ejs模板以下:github
<% include header.ejs %> <div class="hero-unit"> <h1>歡迎來到 Microblog</h1> <p>Microblog 是一個基於 Node.js 的微博系統。</p> <p> <a class="btn btn-primary btn-large" href="/login">登錄</a> <a class="btn btn-large" href="/reg">當即註冊</a> </p> </div> <% include footer.ejs %>
在public文件夾中放入圖片、js/css文件
能夠從個人源碼直接拷貝。
修改routes文件夾index.jssql
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: '首頁' }); //修改了這裏 }); module.exports = router;
此時頁面以下:
好,index已經作好了,而後開始着手製做登陸界面。
登陸操做涉及數據庫,所以首先咱們先安裝數據庫:
相關設置參考網址:
http://blog.csdn.net/sixp512720288/article/details/52472887
下載地址:
http://dl.mongodb.org/dl/win32/x86_64
安裝包名字:
mongodb-win32-x86_64-2008plus-ssl-3.6.0-rc6.zip
個人電腦是win10 64位的,可能與大家的狀況不同,上面的數據庫的安裝與運行僅供參考。
用到數據庫前都須要啓動:
運行cmd,若是已經按上文的網址配置,去到數據庫的bin文件夾運行,-dbpath後的路徑請按照你的具體配置修改。
請記住如下代碼,啓動網站前記得都要先啓動了數據庫,建議如今就先啓動避免一會忘了啓動報錯
.\mongod.exe -dbpath "E:\mongodb\data\db"
使用數據庫前,還須要安裝一些依賴,進行一些設置:
首先,給package.json一行代碼(不知道寫在哪的,具體可參考源碼):
"mongodb": ">=0.9.9"
cmd
npm i
建立一個settings.js文件
module.exports = {
cookieSecret:'microblogbyvoid', //用於cookie的加密 db:'microblog', //數據庫的名字 host:'localhost', //數據庫地址 }
建立models文件夾,在此文件夾中建立db.js
var settings = require('../settings.js'), Db = require('mongodb').Db, Connection = require('mongodb').Connection, Server = require('mongodb').Server; module.exports = new Db(settings.db, new Server(settings.host, 27017, {}), {safe: true});
接下來須要將用戶數據存儲到數據庫中,你以爲須要作些什麼呢?
爲了將用戶數據存儲到數據庫中,作出以下配置:
"express-session": "^1.15.6", "connect-mongo": ">= 0.1.7"
cmd
npm i
對app.js進行修改,新增:
var session = require('express-session'); var MongoStore = require('connect-mongo')(session); var settings = require('./settings');
以及
app.use(session({ secret:settings.cookieSecret, store:new MongoStore({ // db:settings.db url: 'mongodb://localhost/microblog' }) }));
接下來是註冊頁面以及登陸界面
在views頁面新建reg.ejs模版
<% include header.ejs %>
<form class="form-horizontal" method="post" > <fieldset> <legend>用戶註冊</legend> <div class="control-group"> <label class="control-label" for="username">用戶名</label> <div class="controls"> <input type="text" class="input-xlarge" id="username" name="username"> <p class="help-block">你的賬戶的名稱,用於登錄和顯示。</p> </div> </div> <div class="control-group"> <label class="control-label" for="password">口令</label> <div class="controls"> <input type="password" class="input-xlarge" id="password" name="password"> </div> </div> <div class="control-group"> <label class="control-label" for="password-repeat">重複輸入口令</label> <div class="controls"> <input type="password" class="input-xlarge" id="password-repeat" name="password-repeat"> </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary">註冊</button> </div> </fieldset> </form> <% include footer.ejs %>
在views頁面新建login.ejs模版
<% include header.ejs %>
<form class="form-horizontal" method="post"> <fieldset> <legend>用戶登入</legend> <div class="control-group"> <label class="control-label" for="username">用戶名</label> <div class="controls"> <input type="text" class="input-xlarge" id="username" name="username"> </div> </div> <div class="control-group"> <label class="control-label" for="password">口令</label> <div class="controls"> <input type="password" class="input-xlarge" id="password" name="password"> </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary">登入</button> </div> </fieldset> </form> <% include footer.ejs %>
修改index.js,引入模版渲染頁面
router.get('/reg', function(req, res, next) { res.render('reg', { title: '用戶註冊' }); }); router.get('/login', function(req, res, next) { res.render('login', { title: '用戶登入' }); });
啓動網頁效果以下:
至此註冊、登錄頁面均能正常顯示了,是時候給頁面增長一些響應功能了。
先作註冊界面的功能:
須要作些什麼呢?
- 驗證用戶名是否存在-這個須要讀取數據庫的內容,進行比對
- 驗證密碼是否一致
- 進行必要的密碼保護
- 驗證無誤後將用戶名和密碼保存到數據庫
- 無論驗證結果是正確仍是錯誤,提交頁面後要給出一個反饋
這裏主要對涉數據庫的操做進行一下分析:
驗證用戶名須要讀取數據庫的用戶信息,而保存用戶名和密碼到數據庫是要新增數據庫的用戶信息。這些功能能夠抽離出來,
用User這個構造函數實現這些功能。
User.get()用於獲取用戶信息,
user.save()用於將特定實例保存到數據庫。
因爲代碼用到crypto、user,先添加模塊(其實我是寫完了post才添加的,不過爲了不後面添加時你們都忘了以前的代碼了,先添加了),在index.js添加:
var crypto = require('crypto'); var User = require('../models/user.js');
而後,代碼先寫成這樣:
router.post('/reg',function(req,res,next){ var md5 = crypto.createHash('md5'); var password = md5.update(req.body.password); var newUser =new User({ name:req.body.username, password:password }); User.get(newUser.name,function(err,user){ if(err){ //反饋錯誤,跳轉到/reg,記得return } //判斷用戶是否存在 if(user){ //反饋用戶存在,跳轉到/reg,記得return } //判斷密碼是否一致 if(req.body.password !== req.body['password-repeat'] ){ //反饋密碼不一致,跳轉到/reg,記得return } //用戶不存在 newUser.save(function(err){ if(err){ //反饋錯誤,跳轉到/reg,記得return } //反饋註冊成功,跳轉到/。 }); }); });
代碼寫到這,還有一些問題沒有解決:
先解決構造函數的問題:
models文件夾新建user.js
var mongodb = require('./db.js'); function User(user){ this.name = user.name; this.password = user.password; } User.prototype.save = function(callback){ //存入mongodb文檔 var user = { name:this.name, password:this.password } mongodb.open(function(err,db){ if(err){ return callback(err); } // 讀取users集合 db.collection('users',function(err,collection){ if(err){ mongodb.close(); return callback(err); } //給name添加索引 collection.ensureIndex('name',{unique:true}); //寫入user文檔 collection.insert(user,{safe:true},function(err,user){ mongodb.close(); callback(err,user); }); }); }); }; User.get = function(username,callback){ mongodb.open(function(err,db){ if(err){ callback(err); } //讀取users集合 db.collection('users',function(err,collection){ if(err){ mongodb.close(); return callback(err); } //查找name屬性爲username的文檔 collection.findOne({name:username},function(err,doc){ mongodb.close(); if(doc){ //封裝文檔爲User對象 var user = new User(doc); callback(err,user); } else { callback(err,null); } }); }); }); }; module.exports = User;
寫到這裏,先嚐試簡單驗證一下User函數是否存在問題。
將代碼修改成:(是修改不是新增)
router.post('/reg',function(req,res,next){ var md5 = crypto.createHash('md5'); var password = md5.update(req.body.password); var newUser =new User({ name:req.body.username, password:password }); User.get(newUser.name,function(err,user){ if(err){ //反饋錯誤,跳轉到/reg,記得return console.log(err); return res.redirect('/reg'); } //判斷用戶是否存在 if(user){ console.log('user existed'); return res.redirect('/reg'); } //判斷密碼是否一致 if(req.body.password !== req.body['password-repeat'] ){ //反饋密碼不一致,跳轉到/reg,記得return console.log('password not equal'); return res.redirect('/reg'); } //用戶不存在 newUser.save(function(err){ if(err){ //反饋錯誤,跳轉到/reg,記得return console.log('save failure'); return res.redirect('/reg'); } console.log('save success'); res.redirect('/'); }); }); });
這個能夠自行測試,就不截圖了。
至此,註冊還有一個反饋的功能未實現。
爲此,引入新的模塊,
package.json:
"connect-flash": "^0.1.1"
cmd
npm i
app.js
var flash = require('connect-flash'); app.use(flash()); app.use(function(req,res,next){ console.log('app.user local'); res.locals.user = req.session.user; res.locals.post = req.session.post; var error = req.flash('error'); res.locals.error = error.length ? error:null; var success = req.flash('success'); res.locals.success = success.length ? success : null; next(); });
再次修改index.js
router.post('/reg',function(req,res,next){ var md5 = crypto.createHash('md5'); var password = md5.update(req.body.password); var newUser =new User({ name:req.body.username, password:password }); User.get(newUser.name,function(err,user){ if(err){ //反饋錯誤,跳轉到/reg,記得return req.flash('error',err); return res.redirect('/reg'); } //判斷用戶是否存在 if(user){ req.flash('error','用戶已存在'); return res.redirect('/reg'); } //判斷密碼是否一致 if(req.body.password !== req.body['password-repeat'] ){ //反饋密碼不一致,跳轉到/reg,記得return req.flash('error','密碼不一致'); return res.redirect('/reg'); } //用戶不存在 newUser.save(function(err){ if(err){ //反饋錯誤,跳轉到/reg,記得return req.flash('error','保存失敗'); return res.redirect('/reg'); } req.flash('success','保存成功'); res.redirect('/'); }); }); });
同時,在header.ejs結尾處添加以顯示反饋:
<div id="container" class="container"> <% if (success) { %> <div class="alert alert-success"> <%= success %> </div> <% } %> <% if (error) { %> <div class="alert alert-error"> <%= error %> </div> <% } %>
如今能夠先測試一下,應該已經能夠註冊,而且每次註冊均會有反饋。
而後就是登入/登出的頁面
上面,登入界面已經作好了,登出直接點擊就登出了,不須要額外製做界面。可是如今頁面沒有登出的界面,須要加上去。登出的按鈕只在登錄後纔出現。
爲此,能夠修改header.ejs
<ul class="nav"> <li class="active"><a href="/">首頁</a></li> <% if (!user) { %> <li><a href="/login">登入</a></li> <li><a href="/reg">註冊</a></li> <% } else { %> <li><a href="/logout">登出</a></li> <% } %> </ul>
作登入的響應,在index.js添加以下代碼:
router.post('/login',function(req,res,next){ var md5 = crypto.createHash('md5'); var 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','登入成功'); return res.redirect('/'); }); }); router.get('/logout',function(req,res,next){ req.session.user=null; req.flash('success','登出成功'); res.redirect('/'); });
至此,登入登出功能已經完成。
接下來,對頁面權限進行控制:
在index.js中添加
function checkLogin(req,res,next){ if(!req.session.user){ req.flash('error','用戶未登陸'); return res.redirect('/login'); } next(); } function checkNotLogin(req,res,next){ if(req.session.user){ req.flash('error','用戶已登陸'); return res.redirect('/'); } next(); }
並將代碼修改成以下,可參考源碼:
接着就是微博的界面了
爲了方便,先作了微博模型,與User相似的Post。它的功能也是獲取與保存,只不過數據從用戶信息變成了發表的微博信息。
首先,咱們來思考一下,Post具體要作什麼呢?
Post建立的對象應該包含微博正文、用戶名、時間這三個信息;
用戶發信息的時候,實例post.save()須要保存微博正文、用戶名、時間這三個信息,這些信息都包含在實例中了,所以能夠不傳進去,只需設置一個callback(err)便可。
Post.get是獲取微博,這裏的設想是有兩種模式,一種是指定用戶獲取,一種是獲取所有,所以其能夠傳入用戶名或者null(顯示所有),另外須要一個callback。
var mongodb = require('./db'); function Post(username,post,time){ this.user= username; this.post =post; if(time){ this.time = time; }else { this.time = new Date(); } }; module.exports = Post; Post.prototype.save = function save(callback){ //存入Mongodb 的文檔 var post = { user:this.user, post:this.post, time:this.time }; mongodb.open(function(err,db){ if(err){ return callback(err); } //讀取posts集合 db.collection('posts',function(err,collection){ if(err){ mongodb.close(); return callback(err); } //爲user屬性添加索引 collection.ensureIndex('user'); //寫入post文檔 collection.insert(post,{safe:true},function(err,post){ mongodb.close(); callback(err); }); }); }); }; Post.get =function get(username,callback){ mongodb.open(function(err,db){ if(err){ return callback(err); } //讀取posts集合 db.collection('posts',function(err,collection){ if(err){ mongodb.close(); return callback(err); } //查找user屬性爲username的文檔,若是username是null則匹配所有 var query = {}; if(username) { query.user = username; } collection.find(query).sort({time:-1}).toArray(function(err,docs){ mongodb.close(); if(err){ callback(err,null); } //封裝posts爲Post對象 var posts = []; docs.forEach(function(doc,index){ var post = new Post(doc.user,doc.post,doc.time); posts.push(post); }); callback(null,posts); }); }); }); };
修改index.ejs,用於顯示微博文章。
<% include header.ejs %> <% if (!user) { %> <div class="hero-unit"> <h1>歡迎來到 Microblog</h1> <p>Microblog 是一個基於 Node.js 的微博系統。</p> <p> <a class="btn btn-primary btn-large" href="/login">登錄</a> <a class="btn btn-large" href="/reg">當即註冊</a> </p> </div> <% } else { %> <% include say.ejs %> <% } %> <% include posts.ejs %> <% include footer.ejs %>
這裏用到了say.ejs以及posts.ejs,能夠參考源碼,由於這個模板後面也不須要修改了,就不列出來了。
修改index.js,傳入微博信息給模板:
var Post = require('../models/post.js'); /* GET home page. */ router.get('/', function(req, res, next) { Post.get(null,function(err,posts){ if(err){ posts=[]; } res.render('index', { title: '首頁', posts: posts, }); }); }); //用於發表微博 router.post('/post',checkLogin); router.post('/post',function(req,res,next){ var currentUser = req.session.user; var post =new Post(currentUser.name,req.body.post); post.save(function(err){ if(err){ req.flash('error',err); return res.redirect('/'); } req.flash('success',"發表成功"); res.redirect('/u/'+currentUser.name); }); });
還須要加入一個用戶界面:
user.ejs
接着在index.js添加其響應
router.get('/u/:user',function(req,res){ User.get(req.params.user,function(err,user){ if(!user){ req.flash('error','用戶不存在'); res.redirect('/'); } Post.get(user.name,function(err,posts){ if(err){ req.flash('/'); return redirect('error',err); } res.render('user',{ title:user.name, posts:posts }); }); }); });
至此,整個微博的案例基本完成了。
參考文獻:
http://www.cnblogs.com/yuanzm/p/3770986.html
http://blog.csdn.net/sixp512720288/article/details/52472887
http://cnodejs.org/topic/50367e6ff767cc9a51d2e021