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

完整代码下载

https://github.com/haishangfeie/weibo

开发详细步骤

创建项目:

express -e microblog

 
按提示输入

PS E:\code\nodejsExercise\express\3> cd .\microblog\
PS E:\code\nodejsExercise\express\3\microblog> npm i

现在,我们先启动网站看看

npm start

 
如果能运行到以上的效果,那么项目已经创建好了。

功能分析

那么在正式开始创建网站前,我也试着对接下来的项目进行一个功能分析。 
本项目是一个微博项目的简单实现,需要包括如下功能:用户的登录、注册、退出登录,另外还有信息登录功能。 
大致规划:

  • 一个主页用于显示微博的主体内容
  • 一个登录页面
  • 一个注册页面
  • 一个用户页面,只显示用户的微博信息

接下来开始正式的搭建项目。

可以先写一个index.html页面看看效果: 

接着将它改为模板: 
在views文件夹新建一个header.ejs

<!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.ejs

    <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模板如下:

<% 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.js

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});

接下来需要将用户数据存储到数据库中,你觉得需要做些什么呢? 
为了将用户数据存储到数据库中,做出如下配置:

  • 新增一个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

75.《nodejs开发指南》express4.x版-微博案例完整实现的更多相关文章

  1. 《nodejs开发指南》微博实例express4.x版

    之前一直执着于前端开发,最近几天,开始学起了nodejs.作为一名前端开发者,见到这样一门用javascript写的后台自然是很激动的.但是,后台毕竟不同于前端,在学习的过程中,还是会遇到不少问题. ...

  2. 使用express4.x版、Jade模板以及mysql重写《nodejs开发指南》微博实例

    最近阅读<nodejs开发指南>一书,书是不错的,然而其微博代码示例用的是express3.x,用些过时了,运行代码出现不少bug(我电脑安的是express4.x),于是用express ...

  3. 《NodeJS开发指南》第五章微博实例开发总结

    所有文章搬运自我的个人主页:sheilasun.me <NodeJS开发指南>这本书用来NodeJS入门真是太好了,而且书的附录部分还讲到了闭包.this等JavaScript常用特性.第 ...

  4. 《NodeJs开发指南》第五章微博开发实例的中文乱码问题

    在<NodeJs开发指南>第五章,按照书中的要求写好微博实例后,运行代码,发现中文显示出现乱码,原因是:views文件夹下的ejs文件的编码格式不是utf-8. 解决方法:以记事本方式打开 ...

  5. nodejs开发指南读后感

    nodejs开发指南读后感 阅读目录 使用nodejs创建http服务器; supervisor的使用及nodejs常见的调式代码命令了解; 了解Node核心模块; ejs模板引擎 Express 理 ...

  6. 读《nodejs开发指南》记录

    最近看了一下<nodejs开发指南>发现nodejs在某些特定的领域由他自己的长处,适合密集计算但是业务逻辑比较简单的场景,如果做网站还是选择php吧,呵呵,这本书我除了第5章<用n ...

  7. nodejs 开发指南 书中小项目 代码

    最近 在学习node.js 先看了下语法 ,然后就看这个开发指南感觉书还是很有用,但是代码太旧了,网上也没有最新的,所以就自己查着前人的痕迹和自己修改,现在可以跑了. https://github.c ...

  8. VSTO开发指南(VB2013版) 第一章 Office对象模型

    完美地将visual basic和office 办公软件结合起来.来自微软公司VSTO小组的权威专家所编著. 全书共712页,内容极其全面而深入,猛一看,厚地犹如庞然大物.看完离大神就不远了哦< ...

  9. Nodejs开发指南-笔记

    第三章 异步式I/O与事件编程3.1 npm install -g supervisor supervisor app.js 当后台修改代码后,服务器自动重启,生效修改的代码,不用手动停止/启动3.2 ...

随机推荐

  1. 9-第一个app项目

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  2. Internet Explorer Developer Channel 自动化测试 IE 浏览器

    IE 原生 Web Driver 调用,通过简单配置,即可自动化测试 IE 浏览器(目前仅限 Internet Explorer Developer Channel 版本).做一些自动化的操作,都是很 ...

  3. 不再安全的 OSSpinLock

    自旋锁的本质是持续占有cpu,直到获取到资源.与其他锁的忙等待的实现机制不同. 昨天有位开发者在 Github 上给我提了一个 issue,里面指出 OSSpinLock 在新版 iOS 中已经不能再 ...

  4. Raspberry Pi - 调整你的SD卡分割区的大小

    在使用Win32DiskImager为一张空白的SD卡刷入新的Rasbian系统后,卡上的可用剩余空间并不大, 本人有一张8G的SD卡,但是刷入4.1的Rasbian后,用df -h查看,根目录下的空 ...

  5. vue+element的表格分页和前端搜索

    1.前端后台管理会存在很多表格,表格数据过多就需要分页;2.前端交互每次搜索如果都请求服务器会加大服务器的压力,所以在数据量不是很大的情况下可以一次性将数据返回,前端做检索3.下面贴上一个demo & ...

  6. unity调用Android的两种方式:其二,调用aar包

    上一篇我们讲了unity如何调用jar包 http://www.cnblogs.com/Jason-c/p/6743224.html, 现在我们介绍一下怎么生成aar包和unity怎么调用aar 一. ...

  7. 【转】CentOS下firefox安装flash说明

    http://www.cnblogs.com/lamper/archive/2013/01/16/2862254.htm CentOS下自带了firefox,但没有flash插件的,按它自己的提示安装 ...

  8. ArcGIS Engine获得要素的中心点坐标

    IPoint centerPoint =new PointClass();//获得要素的中心点 IArea pArea = pFeature.Shape as IArea; pArea.QueryCe ...

  9. JS学习十七天----工厂方法模式

    工厂方法模式 前言 今天自己看了一下自己写的部分博客,发现写的好丑....開始注意自己的排版!!可是偏亮也不是一朝一夕就完毕的,我尽量让它美丽一点.....每天美丽一点点 正文 工厂方法模式是一种实现 ...

  10. url与图片

    http://restapi.amap.com/v3/staticmap?location=116.481485,39.990464&zoom=10&size=750*300& ...