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

  • 相关阅读:
    索引法则--少用OR,它在连接时会索引失效
    索引法则--LIKE以%开头会导致索引失效进而转向全表扫描(使用覆盖索引解决)
    索引法则--字符串不加单引号会导致索引失效
    索引法则--IS NULL, IS NOT NULL 也无法使用索引
    tomcat管理模块报401 Unauthorized
    MySQL报Too many connections
    JDBC连接MySql例子
    linux安装jdk并设置环境变量(看这一篇文章即可)
    深度解析Java可变参数类型以及与数组的区别
    MySQL真正的UTF-8字符集utf8mb4
  • 原文地址:https://www.cnblogs.com/sharpest/p/8093963.html
Copyright © 2011-2022 走看看