zoukankan      html  css  js  c++  java
  • 75.《nodejs开发指南》express4.x版-微博案例完整实现

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

    完整代码下载

    https://github.com/haishangfeie/weibo

    开发详细步骤

    创建项目:

    express -e microblog

    创建项目的截图 
    按提示输入

    PS E:code
    odejsExerciseexpress3> cd .microblog
    PS E:code
    odejsExerciseexpress3microblog> npm i

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

    npm start

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

    功能分析

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

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

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

    可以先写一个index.html页面看看效果: 
    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页面-服务器 
    好,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:mongodbdatadb"

    使用数据库前,还需要安装一些依赖,进行一些设置: 
    首先,给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

  • 相关阅读:
    108. Convert Sorted Array to Binary Search Tree
    How to check if one path is a child of another path?
    Why there is two completely different version of Reverse for List and IEnumerable?
    在Jenkins中集成Sonarqube
    如何查看sonarqube的版本 how to check the version of sonarqube
    Queue
    BFS广度优先 vs DFS深度优先 for Binary Tree
    Depth-first search and Breadth-first search 深度优先搜索和广度优先搜索
    102. Binary Tree Level Order Traversal 广度优先遍历
    How do I check if a type is a subtype OR the type of an object?
  • 原文地址:https://www.cnblogs.com/sharpest/p/8093963.html
Copyright © 2011-2022 走看看