75.《nodejs开发指南》express4.x版-微博案例完整实现
转自: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版-微博案例完整实现的更多相关文章
- 《nodejs开发指南》微博实例express4.x版
之前一直执着于前端开发,最近几天,开始学起了nodejs.作为一名前端开发者,见到这样一门用javascript写的后台自然是很激动的.但是,后台毕竟不同于前端,在学习的过程中,还是会遇到不少问题. ...
- 使用express4.x版、Jade模板以及mysql重写《nodejs开发指南》微博实例
最近阅读<nodejs开发指南>一书,书是不错的,然而其微博代码示例用的是express3.x,用些过时了,运行代码出现不少bug(我电脑安的是express4.x),于是用express ...
- 《NodeJS开发指南》第五章微博实例开发总结
所有文章搬运自我的个人主页:sheilasun.me <NodeJS开发指南>这本书用来NodeJS入门真是太好了,而且书的附录部分还讲到了闭包.this等JavaScript常用特性.第 ...
- 《NodeJs开发指南》第五章微博开发实例的中文乱码问题
在<NodeJs开发指南>第五章,按照书中的要求写好微博实例后,运行代码,发现中文显示出现乱码,原因是:views文件夹下的ejs文件的编码格式不是utf-8. 解决方法:以记事本方式打开 ...
- nodejs开发指南读后感
nodejs开发指南读后感 阅读目录 使用nodejs创建http服务器; supervisor的使用及nodejs常见的调式代码命令了解; 了解Node核心模块; ejs模板引擎 Express 理 ...
- 读《nodejs开发指南》记录
最近看了一下<nodejs开发指南>发现nodejs在某些特定的领域由他自己的长处,适合密集计算但是业务逻辑比较简单的场景,如果做网站还是选择php吧,呵呵,这本书我除了第5章<用n ...
- nodejs 开发指南 书中小项目 代码
最近 在学习node.js 先看了下语法 ,然后就看这个开发指南感觉书还是很有用,但是代码太旧了,网上也没有最新的,所以就自己查着前人的痕迹和自己修改,现在可以跑了. https://github.c ...
- VSTO开发指南(VB2013版) 第一章 Office对象模型
完美地将visual basic和office 办公软件结合起来.来自微软公司VSTO小组的权威专家所编著. 全书共712页,内容极其全面而深入,猛一看,厚地犹如庞然大物.看完离大神就不远了哦< ...
- Nodejs开发指南-笔记
第三章 异步式I/O与事件编程3.1 npm install -g supervisor supervisor app.js 当后台修改代码后,服务器自动重启,生效修改的代码,不用手动停止/启动3.2 ...
随机推荐
- Python语法篇:
- 基础篇: - 介绍 - 下载安装以及PyCharm安装 - 变量 - 数据类型 - 列表,元组,字典,集合 - 函数 - 内置函数 - 生成器,迭代器,装饰器 - 面向对象: - 面向对象简介: ...
- Asp.Net中使用水晶报表(中)
Asp.Net中使用水晶报表(中) 使用Pull模式 我们将通过下面的这些步骤来通过Pull模式来执行水晶报表 1.首先创建rpt文件,并使用水晶报表设计接口设置一些必须的数据连接. 2.拖放一个 C ...
- 点击鼠标右键弹出错误提示:CrashHandler initialization error
电脑重装系统后,什么都没有了,重装部分必须用的软件后,不管是在桌面还是在文件夹中,当点击鼠标右键时,总是弹出错误,如下图所示: 上网找解决方法,也没有找到,但是看错误,是与SVN有关. 产生原因:To ...
- 射击的乐趣:WIN32诠释打飞机游戏
一楼留给链接http://blog.csdn.net/crocodile__/article/details/11860129 楼上神贴,膜拜片刻...... 一.游戏玩法和已经实现的功能 1.打开游 ...
- IBM小机的开机步骤
IBM小机的开机步骤 以下以 IBM P570 小机包含静态逻辑分区(LPAR) 的 Oracle9i RAC环境为例进行讨论 1. 设备(小机.存储.光交......)加电 2. 启动存储 3. 启 ...
- iOS-RAC学习笔记(一)——RACStream
RACStream是RACSignal和RACSequence的父类,定义了一些流的操作方法.从名字上可以看出来,这个对象就像流一样可以往任何一个出口流,同时也可以给这个流设计一道道关卡,改变流(这里 ...
- BZOJ 3277/3473 广义后缀自动机
说实话没啥难的. 建一棵广义后缀自动机,暴力自底向上更新即可. 时间复杂度非常玄学,但据说是可以过的. 要注意每个串中相同的子串的贡献是都要加进去的,开始因为这个被坑了好久 QAQ Code: #in ...
- js对浏览器产生的影响
Js 是单线程执行引擎.在我们动态修改一些属性时会产生两种效果: 1.Repaint ----- 一部分重画,修改 div 的颜色呀,但是尺寸没有改变. 2.Reflow ---- 元素 ...
- Mysql学习总结(1)——常用sql语句汇总
一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...
- 四 HBase 客户端设置缓存优化查询。
其实查询无非是一个 HBase 的 RPC 计算公式 .然后给API 提供值. RPCs = (Rows * Cols per Row) / Min(Cols per Row, Batch Size) ...