zoukankan      html  css  js  c++  java
  • 一个简单的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 });

        

        

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

  • 相关阅读:
    vue打包不显示或图片不显示配置
    Vue::is特性用法
    毕业实习报告
    前端vscode常用快捷键总结
    1. 变量常量
    信息收集之CMS指纹识别
    4. EIGRP的复合度量值
    3. EIGRP报文,三张表,邻居建立
    信息收集之目录扫描
    2. EIGRP路由单播邻居和被动接口
  • 原文地址:https://www.cnblogs.com/yuity/p/5298105.html
Copyright © 2011-2022 走看看