访问数据库
1.1安装MongoDB :Install MongoDB on Linux Systems
解压
设置PATH
1.2运行
创建文件读写目录 data/db
$mongod --dbpath <path to data directory>
Getting Started with MongoDB Production Notes
修改工程目录package.json,添加mongodb模块依赖,然后npm install安装。
在工程目录下创建setting.js文件,保存数据库链接信息:
1 //setting.js 2 module.exports = { 3 cookieSecret: 'microblogbyvoid', 4 db: 'microblog', 5 host: 'localhost', 6 };cookieSecret 用于Cookie加密与数据库无关;db是数据库名称;host是数据库地址;
在工程目录下新建models文件夹,新建db.js:
1 //db.js 2 var settings = require('../settings'); 3 var Db = require('mongodb').Db; 4 var Connection = require('mongodb').Connection; 5 var Server = require('mongodb').Server; 6 7 module.exports = new Db(settings.db, new Server(settings.host, Connection.DE FAULT_PORT, {}));通过module.exports输出了创建的数据库链接。
2.会话支持
Cookie用于保存服务器发送给客户端的唯一标识符,服务器据此识别客户端。
Express提供会话中间件,默认把用户信息存储在内存中,使用MongoDB,则把会话信息存储在数据库中,便于持久维护。首先添加connect-mongo模块(MongoDB session store for Connect)和 express-session模块:修改package.json文件,然后npm install。
修改app.js文件,添加:
12 //mongodb 13 var session = require('express-session'); 14 var MongoStore = require('connect-mongo')(session); 15 var settings = require('./settings');
25 //mongodb-cookie 26 app.use(session({ 27 secret: settings.cookieSecret, 28 store: new MongoStore({ 29 db: settings.db 30 }) 31 }));
session() 提供会话支持,store参数为MongoStore实例,把会话信息存储到数据库中,以避免丢失。可以通过req.session获取当前用户的会话对象,以维护用户的相关信息。
3.注册和登入
3.1注册页面
添加views/reg.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 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>
修改routes/index.js 文件:
17 router.get('/reg', function(req, res) { 18 res.render('reg', { title: '用户注册'}); 19 });
运行效果:
在routes/index.js 中添加/reg响应函数:
router.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.passowrd).digest('base64'); var newUser = new User({ name: req.body.username, password: password, }); //检查用户是否已经存在 User.get(newUser.name, function(err, user) { if(user) err = 'Username already exists.'; 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('/'); }); }); //--User.get });
express3也不再默认支持 flash方法,需要另外安装 connect-flash;
crypto是nodejs的一个核心模块,功能是加密并生成各种散列,使用之前先require声明;
User是设计的用户模型,描述数据的对象,MVC架构中的模型是真正与数据打交道的工具,是框架中最根本的部分。
在models文件夹下创建user.js文件:
3.2视图交互:实现不同登录状态下页面呈现不同的内容
需要创建动态视图助手,通过它才能在视图中访问会话中的用户数据,为了显示错误和成功的信息,也需要在动态视图助手中增加响应函数。
app.js中添加:
35 app.use(function(req, res, next) { 36 res.locals.user = req.session.user; 37 res.locals.err = req.session.error; 38 res.locals.succ = req.session.success; 39 40 next(); 41 });
使用locals和session存储状态变量,然后在视图文件中选择显示。
layout.ejs:
<div class="collapse navbar-collapse">
<ul class="nav navbar-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>
</div><!--/.nav-collapse -->
<div id="container" calss="container">
<% if(succ) { %>
<div class="alert alert-success">
<%= succ %>
</div>
<% } %>
<% if(err) { %>
<div class="alert alert-error">
<%= err %>
</div>
<% } %>
<%- body %>
</div>
index.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 %>
3.3登入和登出
添加下列代码至routes/index.ejs:
//用户登录 router.get('/login', checkNotLogin); router.get('/login', function(req, res) { res.render('login', { title: '用户登入', }); }); router.post('/login', checkNotLogin); router.post('/login', function(req, res) { //生成口令散列值 var md5 = crypto.createHash('md5'); var password = md5.update(req.body.password).digest('base64'); User.get(req.body.username, function(err, user) { if(!user) { err = '用户不存在'; req.session.error = err; return res.redirect('/login'); } if(user.password != password) { err = '用户口令错误'; req.session.error = err; return res.redirect('/login'); } succ = '登入成功'; req.session.success = succ; req.session.user = user; return res.redirect('/'); }); }); /* router.post('/doLogin', function(req, res) { }); */ //用户登出 router.get('/logout', checkLogin); router.get('/logout', function(req, res) { succ = '登出成功'; req.session.success = succ; req.session.user = null; return res.redirect('/'); });
添加views/login.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>
在每个路径前面添加路由中间件,同一个路径绑定多个响应函数,通过next()转移控制权。
在routes/index.js中添加checkNotLogin或者checkLogin函数:
//用户登出 router.get('/logout', checkLogin); router.get('/logout', function(req, res) { succ = '登出成功'; req.session.success = succ; req.session.user = null; return res.redirect('/'); }); //登录状态--路由中间件 function checkLogin(req, res, next) { req.session.error = null; req.session.success = null; if (!req.session.user) { err = '未登入'; req.session.error = err; return res.redirect('/login'); } next(); } function checkNotLogin(req, res, next) { req.session.error = null; req.session.success = null; if(req.session.user) { err = '已登入'; req.session.error = err; return res.redirect('/'); } next(); }
4.发表微博
4.1建立微博模型 创建models/post.js:
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(); } }; 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); }); }); }); } 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); }); }); }); } module.exports = Post;
4.2发表微博:
在routes/index.js中添加控制器
router.post('/reg', checkNotLogin); router.post('/reg', function(req, res) { //检验用户两次输入的口令是否一致 if(req.body['password-repeat'] != req.body['password']) { req.session.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 = 'Username already exists.'; if(err) { //req.flash('error', err); req.session.error = err; return res.redirect('/reg'); } //如果不存在则新增用户 newUser.save(function(err) { if(err) { //req.flash('error', err); req.session.error = err; return res.redirect('/reg'); } req.session.user = newUser; req.session.success = '注册成功'; res.redirect('/'); }); console.log('User exists.!'); }); //--User.get });
4.3 用户视图
新建views/user.ejs和views/posts, views/say.ejs:
user.ejs:首先检查用户是否存在,如果存在则从数据库中获取该用户的微博,然后通过posts属性传递给user视图。
1 <% if (user) { %>
2 <% include say.ejs %>
3 <% } %>
4 <% include posts.ejs %>
say.ejs:显示一个用于发微博的表单
1 <form method="post" action="/post" class="well form-inline center" style="te xt-align:center;">
2 <input type="text" class="span8" name ="post">
3 <button type="submit" class="btn btn-success"><i class="icon-comment icon- white"></i> 发言 </button>
4 </form>
posts.ejs:按照行列现实传入的posts内容
1 <% posts.forEach(function(post, index) {
2 if(index % 3 == 0) { %>
3 <div class="row">
4 <% } %>
5 <div class="span4">
6 <h2><a href="/users/<%= post.user %>"><%= post.user %></a>说</h2>
7 <p><small><%= post.time %></small></p>
8 <p><%= post.post %></p>
9 </div>
10 <% if (index % 3 == 2) { %>
11 </div><!-- end row -->
12 <% } %>
13 <% }) %>
14 <% if (posts.length % 3 != 0) { %>
15 </div><!-- end row -->
16 <% } %>
运行效果
说明:附件中的代码由于附件的大小限制,删除了node_modules文件夹,解压后使用npm install安装即可。该代码的开发环境是Ubuntu 14.04,新增的模块均为2014-8-5之前的最新版。
存在的问题:
页面排版
不能使用中文注册的用户发表微博,可以注册成功,但是发表时出现响应错误,其他用户可以看到发表的内容,建议使用英文字母和其他数字字符组合的用户名
转载于:https://blog.51cto.com/xjhznick/1536784