一个简单的blog系统(一)基本功能的实现

---恢复内容开始---

Node.js+Express+MongoDB+Bootstrap开发微博网站的过程如下

1.建立微博网站基本网站结构

  1.1 为了学习简便,采用和html结构相似的ejs作为模板引擎。

1 express -e myBlog

  1.2 进入项目目录,执行npm install 命令,安装项目所需要的依赖。

  1.3 运行npm start 命令,打开所需浏览器访问localhost:3000,可以看到如下结果,说明项目目录初始化成功。

2.搭建多人博客

  2.1 功能分析

    本实例实现的是微博的基本功能,包括用户注册、登陆、发表微博、显示全部(个人)微博和退出微博几个功能。

  2.2 设计路由规划   

1 /: 主页
2 /login: 登录页
3 /reg: 注册页
4 /post: 发表文章
5 /logout: 登出

  注意:登陆和注册只能在用户未登录状态下使用,发表微博和退出只能在用户登录状态下使用,首页和用户首页在登陆和未登录状态下显示不同的内容。通过会话(session)管理用户登录状态。用户登录、注册、发表成功以及登出后都会回到主页。

  2.3 修改 index.js 如下:

    

var express = require('express');
var router = express.Router();    //创建模块化安装路径的处理程序。

//主页路由
router.get('/', function(req, res) {
  res.render('index', { title: '主页' });
});


//注册页路由
router.get('/reg', function(req, res) {
    res.render('reg', { title: '注册'});
});

router.post('/reg', function(req, res) {

});

//登录页路由
router.get('/login', function(req, res) {
    res.render('login', { title: '登录'});
});

router.post('/login', function(req, res) {

});

//发表文章也路由
router.get('/post', function(req, res) {
    res.render('post', { title: '发表'});
});

router.post('/post', function(req, res) {

});

//登出的路由
router.get('/logout', function(req, res) {

})
module.exports = router;

  2.4 界面设计,使用Bootstrap前端框架布局,根据功能以及路由规划,相应的界面和对应的模板引擎为:   

          首页:index.ejs

    用户首页:user.ejs

    登陆页面:login.ejs

    注册页面:reg.ejs

    公用页面:header.ejs(顶部导航条)、alert.ejs(顶部下方错误信息显示)、foote.ejsr(底部显示)、say.ejs(发布微博)、posts.ejs(按行按列显示已发布的微博)    

    2.4.1 使用bower安装Bootstrap、jQuery框架

bower install bootstrap jquery

3.微博功能的实现

  3.1 访问mongodb数据库

    3.1.1 通过淘宝镜像安装一下模块     

npm install mongodb --save -d --registry=https://registry.npm.taobao.org
npm install express-session --save -d --registry=https://registry.npm.taobao.org
npm install connect-mongo --save -d --registry=https://registry.npm.taobao.org
npm install connect-flash --save -d --registry=https://registry.npm.taobao.org

    3.1.2 在项目根目录下建立settings.js文件,用于保存该项目的配置信息,比如数据库的连接信息。其中,db是数据库的名称,host是数据库的地址,cookieSecret是用来对cookie进行加密,port为数据库的端口号。     

module.exports = {
    cookieSecret: 'myblog',
    db: 'blog',
    host: 'localhost',
    port: 27017
}

    3.1.3  然后在项目的根目录下新建models文件夹,并且在model文件夹下新建db.js,用于创建数据库连接信息。

var settings = require('../settings'),
    Db = require('mongodb').Db,
    Connection = require('mongodb').Connection,
    Server = require('mongodb').Server;
module.exports = new Db(settings.db, new Server(settings.host, settings.port), {
    safe: true
});

  3.2 会话支持(session)   

var settings = require('./settings');
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
app.use(session({
    secret: settings.cookieSecret,
    cookie: {maxAge: 1000*60*60*24*30},
    key: settings.db,
    store: new MongoStore({
        url: 'mongodb://localhost/blog'
    })
}));

  3.3 页面通知(connect-flash)  

//通过淘宝镜像安装connect-flash模块
npm install connect-flash --save -d --registry=https://registry.npm.taobao.org
//然后修改app.js,添加
var flash = require('connect-flash');
//最后应用flash功能
app.use(flash())

  3.4 响应注册

    3.4.1 在models文件夹下新建user.js,添加如下代码:

 1 var mongodb = require('./db');
 2 
 3 //User构造函数,用于创建对象
 4 function User(user) {
 5     this.name = user.name;
 6     this.password = user.password;
 7     this.email = user.email;
 8 };
 9 
10 //输出User对象
11 module.exports = User;
12 
13 //存储用户信息到mongodb数据库
14 User.prototype.save = function(callback) {
15     //要存入数据库的文档
16     var user = {    //用户信息
17         name: this.name,
18         password: this.password,
19         email: this.email
20     };
21 
22     //打开数据库
23     mongodb.open(function(err, db) {
24         if(err) {
25             return callback(err);    //错误,返回err信息
26         }
27         //读取users集合
28         db.collection('users', function(err, collection) {
29             if(err) {
30                 mongodb.close();
31                 return callback(err);    //错误,返回err信息
32             }
33 
34             // 为name属性添加索引
35             // collection.ensureIndex('name', {unique: true});
36             
37             //将user用户数据插入users集合
38             collection.insert(user, {safe: true}, function(err, user) {
39                 mongodb.close();
40                 if(err) {
41                     return callback(err);    //错误,返回err信息
42                 }
43                 callback(null, user[0]);    //成功,err为null,并返回存储后的用户文档
44             });
45         });
46     });
47 };
48 
49 //从数据库中读取用户信息
50 User.get = function(name, callback) {
51     //打开数据库
52     mongodb.open(function(err, db) {
53         if(err) {
54             return callback(err);    //错误,返回err信息
55         }
56 
57         //读取users集合
58         db.collection('users', function(err, collection) {
59             if(err) {
60                 mongodb.close();
61                 return callback(err);    //错误,返回err信息
62             }
63             //查找用户名(name键)值为name的一个文档
64             collection.findOne({name: name}, function(err, user) {
65                 mongodb.close();
66                 if(err) {
67                     return callback(err);    //失败!返回err信息
68                 }
69                 callback(null, user);    //成功,返回查询的用户信息
70             })
71         })
72     })
73 }

    3.4.2 然后打开index.js,在前面添加如下代码:

var crypto = require('crypto');
User = require('../models/user.js');

    3.4.3 其次,修改index.js中的app.post('/reg/)如下所示:

 1 router.post('/reg', function(req, res) {
 2     var name = req.body.name,
 3         password = req.body.password,
 4         password_re = req.body['password-repeat'];
 5 
 6     //检测用户两次输入的密码是否一致
 7     if(password != password_re) {
 8         req.flash('error', '两次输入的密码不一致!');
 9         return res.redirect('/reg');    //返回注册页
10     }
11 
12     //生成密码的md5值
13     var md5 = crypto.createHash('md5'),
14         password = md5.update(req.body.password).digest('hex');
15     //用新注册用户信息对象实例化User对象,用于存储新注册用户和判断注册用户是否存在
16     var newUser = new User({
17         name: req.body.name,
18         password: password,
19         email: req.body.email
20     });
21     //检查用户名是否已经存在
22     User.get(newUser.name, function(err, user) {
23         if(user) {
24             req.flash('error', '用户名已存在!');
25             return res.redirect('/reg');    //返回注册页面
26         }
27         //如果不存在则新增用户
28         newUser.save(function(err, user) {
29             if(err) {
30                 req.flash('error', err);    //保存错误信息,用于界面显示
31                 return res.redirect('/reg');    //注册失败返回注册页面
32             }
33             req.session.user = user;    //将用户信息存入session
34             req.flash('success',  '恭喜你,注册成功!');
35             res.redirect('/');    //注册成功后返回主页
36         });
37     });
38 });

     3.4.4  修改header.ejs,让导航条在已登录和未登录状态下显示不同的内容:

1 <% if(user) { %>
2 <li><a href="/post" title='发表'>发表</a></li>
3 <li><a href="/logout" title='登出'>登出</a></li>
4 <% } else { %>
5 <li><a href="/login" title='登录'>登录</a></li>
6 <li><a href="/reg" title='注册'>注册</a></li>
7 <% } %>

    3.4.5 为了在页面显示注册(或登陆)成功或失败信息,添加如下代码:

 1 <!--显示成功或错误信息-->
 2 <% if(success){ %>
 3 <div class="alert alert-success">
 4     <%= success %>
 5 </div>
 6 <% } %>
 7 <% if(error){ %>
 8 <div class="alert alert-error">
 9     <%= error %>
10 </div>
11 <% } %>

   3.5 登录与登出响应

    3.5.1实现用户登录功能

 1 router.post('/login', function(req, res) {
 2     //生成密码的md5值
 3     var md5 = crypto.createHash('md5'),
 4         password = md5.update(req.body.password).digest('hex');
 5     //检查用户是否存在
 6     User.get(req.body.name, function(err, user) {
 7         if(!user) {
 8             req.flash('error', '用户名不存在!');
 9             return res.redirect('/login');    //用户不存在则跳转到登录页
10         }
11         //检查密码是否一致
12         if(user.password != password) {
13             req.flash('error', '密码错误!');
14             return res.redirect('/login');    //密码错误则跳转到登录页
15         }
16         //用户名密码均匹配后,将用户信息存入session
17         req.session.user = user;
18         req.flash('success', '登录成功!');
19         res.redirect('/');    //登录成功后跳转到主页
20     });
21 });

    3.5.2 实现用户登出响应

1 router.get('/logout', function(req, res) {
2     //通过复制为null,丢掉session中的用户信息,实现用户的退出。
3     req.session.user = null;
4     req.flash('success', '登出成功!');
5     res.redirect('/');    //登出成功后跳转到主页
6 });

  3.6 页面权限控制

  为了实现登陆和注册功能只对未登录的用户有效;发布和退出功能只对已登录的用户有效。如何实现页面权限控制呢?我们可以把用户登录状态检查放到路由中间件 中,在每个路径前增加路由中间件,通过调用next()函数转移控制权,即可实现页面权限控制。因此在index.js中添加checkNotLogin 和checkLogin函数来检测是否登陆,并通过next()转移控制权,检测到未登录则跳转到登录页,检测到已登录则跳转到前一个页。 

 1 function checkLogin(req, res, next) {
 2     if(!req.session.user) {
 3         req.flash('error', '您还没有登录!');
 4         res.redirect('/login');
 5     }
 6     next();    //控制权转移:当不同路由规则向同一路径提交请求时,在通常情况下,请求总是被第一条路由规则捕获,
 7           // 后面的路由规则将会被忽略,为了可以访问同一路径的多个路由规则,使用next()实现控制权转移。
 8 }
 9 
10 function checkNotLogin(req, res, next) {
11     if(req.session.user) {
12         req.flash('error', '您已登录!');
13         res.redirect('back');    //返回之前的页面
14     }
15     next();
16 }

   3.7 为了维护用户状态和flash的通知功能,给每一个ejs模板传入了以下三个值:

user: req.session.user
success: req.flash('success').toString()
error: req.flash('error').toString()

   3.8 发表文章功能实现

    3.8.1 发表页的页面设计

 1 <div class="container">
 2     <form role="form" method="post">
 3         <h2>发表</h2>
 4         <hr/>
 5         <div class="form-group">
 6               <label for="title" class="control-label">标题:</label>
 7              <input type="text" class="form-control" name="title" placeholder="请输入标题名称" required>
 8            </div>
 9         <div class="form-group">
10             <label for="name" class="control-label">正文:</label>
11             <textarea class="form-control" name="post" rows="10" cols="10" required></textarea>
12           </div>
13           <div class="form-group">
14             <button type="submit" class="btn btn-primary">发表</button>
15            </div>
16     </form>
17 </div>

    3.8.2 文章模型

  为了实现发布文章功能,首先创建Post对象,在models文件夹下创建post.js文件,该文件功能与user.js功能类似,用于存储新发布的文章及查询全部或指定用户的文章,post.js代码如下:

 1 var mongodb = require('./db');
 2 
 3 function Post(name, title, post) {
 4     this.name = name;
 5     this.title = title;
 6     this.post = post;
 7 }
 8 
 9 module.exports = Post;
10 
11 //存储一篇文章及其相关信息
12 Post.prototype.save = function(callback) {
13     var date = new Date();
14     //存储各种时间格式,方便以后扩展
15     var time = {
16         date : date,
17         year: date.getFullYear(),
18         month: date.getFullYear() + "-" + (date.getMonth() + 1),
19         day: date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(),
20         minute: date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + 
21             date.getHours() + ":" + (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
22     }
23     //要存入数据库的文档
24     var post = {
25         name: this.name,
26         time: time,
27         title: this.title,
28         post: this.post
29     };
30     //打开数据库
31     mongodb.open(function(err, db) {
32         if(err) {
33             return callback(err);
34         }
35         //读取posts集合
36         db.collection('posts', function(err, collection) {
37             if(err) {
38                 mongodb.close();
39                 return callback(err);
40             }
41 
42             //为user属性添加索引
43             //collection.ensureIndex('user');
44             
45             //将文档插入到posts集合
46             collection.insert(post, {safe: true}, function(err) {
47                 mongodb.close();
48                 if(err) {
49                     return callback(err);    //失败,返回err数据
50                 }
51                 callback(null);    //返回err为空,即是null
52             });
53         });
54     });
55 };
56 
57 Post.get = function(name, callback) {
58     //打开数据库
59     mongodb.open(function(err, db) {
60         if(err) {
61             return callback(err);
62         }
63         //读取posts集合
64         db.collection("posts", function(err, collection) {
65             if(err) {
66                 mongodb.close();
67                 return callback(err);
68             }
69             //查找name属性为name的文章记录,如果name为null则查找全部记录
70             var query = {};
71             if(name) {
72                 query.name = name;
73             }
74             //根据query对象来查询文章,并按照时间排序
75             collection.find(query).sort({time: -1}).toArray(function(err, docs) {
76                 mongodb.close();
77                 if(err) {
78                     return callback(err);    //失败,返回err信息
79                 }
80                 callback(null, docs);    //成功,以数组的形式返回查询的结果
81             });
82         });
83     });
84 };

    3.8.3 发表响应

打开index.js,在User=require('../models/user.js')后添加一行代码:

Post = require('../models/post.js');

然后修改router.post('/post')的代码如下面所示:

 1 router.post('/post', checkLogin);
 2 router.post('/post', function(req, res) {
 3     var currentUser = req.session.user,
 4         post = new Post(currentUser.name, req.body.title, req.body.post);
 5 
 6     post.save(function(err) {
 7         if(err) {
 8             req.flash('error', err);
 9             return res.redirect('/');
10         }
11         req.flash('success', '发布成功!');
12         res.redirect('/');    //发表成功即跳转到首页
13     });
14 });

其次,需要修改index.ejs,让主页显示发表过的文章及其相关信息。打开index.ejs,修改如下所示:

 1 <!-- 发表的文章内容 -->
 2 <div class="list-group">
 3     <%  posts.forEach(function(post, index) { %> 
 4     <div class="list-group-item">
 5         <h4><a href="#"><%= post.title %></a></h4>
 6         <p class="list-group-item-text" style="padding: 10px 0;">
 7             <%- post.post %>
 8         </p>
 9         <p class="info">
10             <a href="#"><%= post.name %></a>&nbsp;&nbsp;发布于:&nbsp;&nbsp;<%= post.time.minute %>
11             <span class='glyphicon glyphicon-comment' style="padding:0 10px;">评论(0)</span>
12             <span class="glyphicon glyphicon-share-alt">阅读(0)</span>
13         </p>
14     </div>
15 <% }); %>

最后,打开index.js,修改router.get('/')代码如下所示:

 1 router.get('/', function(req, res) {
 2     Post.get(null, function(err, posts) {
 3         if(err) {
 4             posts = [];
 5         }
 6         res.render('index', { 
 7             title: '主页',
 8             user: req.session.user,
 9             posts: posts,
10             success: req.flash('success').toString(),
11             error: req.flash('error').toString()
12         });
13     });
14 });

    

    

---恢复内容结束---

原文地址:https://www.cnblogs.com/yuity/p/5298105.html