75.《nodejs開發指南》express4.x版-微博案例完整實現

轉自:https://blog.csdn.net/cgwcgw_/article/details/39317587

完整代碼下載

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頁面看看效果: 
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頁面-服務器 
好,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});

 

接下來須要將用戶數據存儲到數據庫中,你以爲須要作些什麼呢? 
爲了將用戶數據存儲到數據庫中,作出以下配置:

  • 新增一個connect-mongo模塊:
"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 } //反饋註冊成功,跳轉到/。 }); }); });

代碼寫到這,還有一些問題沒有解決:

  • User構造函數未定義
  • 反饋未實現

先解決構造函數的問題: 
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

相關文章
相關標籤/搜索