node express mongoDB个人博客总结

1 注册、登录和退出

1.1 用户注册、登录

  1. 设置模板引擎、mongoDB数据库驱动、静态文件途径和post要求剖析中间件

  2. 一致api.js路由的数据返回花样

    // 一致返回数据花样
    var responseData;
    // 每次要求进来都举行初始化
    router.use(function (res, req, next) {
      responseData = {
        code: 0,     // 状况码,默以为0
        message: ''   //  状况码的提醒信息,默以为0
      };
    
      next();   // 挪用next()交由下一个中间件继承处置惩罚
    });
  3. 设想用户的数据模子设想与建立

    var mongoose = require('mongoose');
    var Schema = mongoose.Schema;
    
    // userSchema代表名为用户的collection鸠合
    var usersSchema = new Schema({
      // 每一个属性代表collection中的每一个document
    
      // 用户名: 字符串
      username: String,
      // 暗码: 字符串
      password: String,
      // 是不是为治理员,默以为false
      isAdmin: {
        type: Boolean,
        default: false
      }
    });
    
    // 对外导出定义的用户的collection组织
    module.exports = usersSchema;
    var mongoose = require('mongoose');
    // 加载建立的usersSchema模子
    var usersSchema = require('../schemas/users');
    
    // 应用usersSchema建立Model,运用mongoose.model()要领
    // 第一个参数是模子的名字;第二个参数是建立模子的数据组织
    // User是一个组织函数,能够应用Model直接操纵collection,也能够实例化对象来操纵每一个document
    var User = mongoose.model('User', usersSchema);
    
    // 对外暴露模子,提供给营业逻辑操纵
    module.exports = User;
  4. 完成注册逻辑

    • 前端将数据提交到指定路由(Ajax或整页革新)

    • 服务器猎取提交的数据,举行基础考证与数据库查重考证

    • 假如数据库顶用户名已存在,返回毛病信息;假如不存在,则保留当前注册信息

  5. 完成登录逻辑

    • 前端利将数据提交到指定路由(Ajax或整页革新)

    • 服务器经由过程body-parser中间件猎取post要求中的数据req.body;经由过程req.query猎取get要求中的数据,举行基础考证

    • 举行数据库查询考证:运用usernamepassword两个字段举行查询,假如存在,则返回登录胜利;不然登录失利

    • 同时,为登录胜利的用户发送一个cookie,保留必要的信息,但不能是暗码等敏感信息,用于保留用户的登录状况,cookie只要在浏览器没有上传cookie是才发送,而且只发送一次,注重设置逾期时候

      // 设置Cookie,每一个要求进入路由处置惩罚前,先处置惩罚req对象中的cookie信息
      // 用户不管什么时候接见站点,都通经由过程这个中间件,而且经由过程next()要领将返回值通报下去
      // 在登录胜利后,经由过程cookies.set()要领将cookie一同返回给浏览器
      // 第一次登录时,客户端没有cookie,须要发送一个cookie归去;
      app.use(function (req, res, next) {
        req.cookies = new Cookies(req, res);
      
        // 在接见admin中批评、留言等功用时都须要用到登录信息,定义一个全局的req对象的属性,来保留用户登录的cookie信息
        req.userInfo = {};
      
        if(req.cookies.get('userInfo')) {
      try {
        req.userInfo = JSON.parse(req.cookies.get('userInfo'));  // 将cookie剖析为一个对象
        // 须要及时猎取当前的用户是不是为治理员,查询数据库,应用当前用户的_id查询
        User.findById({_id: req.userInfo._id}).then(function (userInfo) {
       req.userInfo.isAdmin = Boolean(userInfo.isAdmin);  // 新增req对象的一个全局属性,推断是不是非治理员
       next();
        })
      } catch (e) {
        next();
      }
        } else {
      next();
        }
      });
    // 发送一个cookie,浏览器会缓存cookie,今后每次要求,浏览器都邑带上这个cookie
    // cookie应当能唯一标识一个用户,所以运用用户信息作为cookie,cookie是一个字符串
    req.cookies.set('userInfo', JSON.stringify({
      _id: userInfo._id,
      username: userInfo.username
    }));
    • 革新页面时,浏览器将cookie中的数据发送到服务器,服务器应用cookie中的信息完成登录页面的展示

    • 应用cookie推断是不是 为治理员(是不是是治理员的信息平常不放在cookie中),登录背景治理界面

1.2 退出

应用cookies模块,将cookie字段设置为null,然后在客户端革新页面,即未登录状况下展示的页面

router.get('/', function (req, res, next) {
  res.render("admin/index.html", {
    userInfo: req.userInfo
  });
});

2 背景治理

2.1 推断是不是为治理员

  • 应用cookie中增添的后续信息,推断是不是为治理员,只要治理员才继承后续操纵

    // 应用中间件推断是不是为治理员账户
    router.use(function (req, res, next) {
      if(!req.userInfo.isAdmin) {
        res.send("Sorry, it's only for Administor!");
        return;
      }
      // 假如是治理员,继承下面的操纵
      next();
    });

2.2 背景信息展示界面

  • 应用get要求,猎取背景页面,传入cookie中的信息req.userInfo

    router.get('/', function (req, res, next) {
      res.render("admin/index.html", {
        userInfo: req.userInfo
      });
    });

2.3 用户信息的展示

  • 一样应用get要求,从数据库中查询一切的用户信息,并将数据返回前端。

  • 分页展示数据的功用经由过程:limit()skip()束缚完成

  • 倒序经由过程sort({_id: -1})束缚完成

  • 同时查询关联字段的数据经由过程.populate(['category', 'article'])完成

    router.get('/user', function (req, res, next) {
    /* 从数据库中读取数据,每页展示的数据数目雷同,内容不雷同
     *   1、limit()束缚限定掏出数据的条数
     *   2、skip()束缚限定最先取数据的位置,skip(2)示意疏忽前两天数据,从第三条最先取
     *   3、每页显现4条
     *     第1页: skip(0)  --> 当前页 - 1 * limit
     *     第2页: skip(4)
     *     第3页: skip(8)
     */
    var page = req.query.page || 1;   // 假如用户不传,默以为第1页
    var limit = 10;
    var pages = 0;   // 保留总的页数
    
    User.count().then(function (count) {
      pages = Math.ceil(count / limit);
      // 当前页数不能大于总页数pages
      page = Math.min(page, pages);
      // 当前页数不能小于1
      page = Math.max(1, page);
    
      var skip = (page - 1) * limit;   // 盘算page以后,肯定须要skip的个数
    
      User.find().limit(limit).skip(skip).then(function (users) {
    res.render('admin/user_index', {
      userInfo: req.userInfo,
      users: users,
    
      count: count,   // 总的数据条数
      pages: pages,   // 总页数
      limit: limit,   // 每页显现几条数据
      page: page     // 当前页数
    });
      })
    })
    });

2.4 分类信息的增添

  1. 构建分类信息的数据组织和模子

    var mongoose = require('mongoose');
    var Schema = mongoose.Schema;
    
    // categoriesSchema代表名为用户的collection鸠合
    var categoriesSchema = new Schema({
      // 每一个属性代表collection中的每一个document
      // 分类称号
      name: String
    });
    
    // 对外导出定义的用户的collection组织
    module.exports = categoriesSchema;
    var mongoose = require('mongoose');
    var categoriesSchema = require('../schemas/categories');
    
    var Category = mongoose.model('Category', categoriesSchema);
    
    module.exports = Category;
  2. 完成get路由猎取展示分类的页面(包含修正和删除分类的进口):经由过程数据库查询猎取一切的分类信息,一样应用limit()skip()完成分页

    Category.find().sort({_id: -1}).limit(limit).skip(skip).then(function (categories){...}
  3. 完成get路由猎取分类增添的页面,应用post要求提交数据

  4. 后端经由过程post路由猎取增添分类的数据,举行基础考证与数据库查重:假如经由过程,则保留数据;不然返回毛病信息。应用findOne()要领查询一条纪录;

  5. new Model().save()要领保留数据

    // 分类增添的数据提交后保留
    router.post('/category/add', function (req, res, next) {
      // 假如用户没有输入数据,或提交的数据不符合需求的花样
      // 没有运用Ajax,所以不符应时直接跳转到别的一个毛病页面
      var category = req.body.category || '';
      if(!req.body.category) {    // 假如称号为空,跳转到毛病页面
        res.render('admin/error', {
          userInfo: req.userInfo,
          message: "分类称号不能为空"
        });
        return;
      }
      // 数据库中是不是已存在雷同的分类称号
      Category.findOne({name: category}).then(function (rs) {
        // 数据库中已存在该分类
        if(rs) {
          res.render('admin/error', {
            userInfo: req.userInfo,
            message: "分类已存在"
          });
          return Promise.reject();   // 退出异步实行
        } else {
          // 数据库中不存在该分类,建立Category的实例对象保留到数据库
          return new Category({
            name: category
          }).save();
        }
      }).then(function (newCategory) {
        res.render('admin/success', {   // 衬着分类胜利的页面
          userInfo: req.userInfo,
          message: "分类保留胜利",
          url: '/admin/category'
        });
      });
    });

2.5 分类信息的修正与删除

经由过程分类信息展示页的进口,经由过程get要求完成分类修正页面复原;再应用post要求将修正的数据举行更新

  1. 前端经由过程url传入修正和删除分类的_id

  2. 经由过程数据库查询到该条纪录,返回原有数据,衬着到编辑分类页面,展示本来的分类称号

  3. 分类修正后,应用post要求上传数据:_id和修正内容

  4. 背景拿到数据后,先举行基础考证;数据库考证(查询分类名是不是已存在)

      Category.findOne({
          id: {$ne: id},    // 差别的纪录中是不是存在雷同的分类称号
          name: nameCategory
        })
  5. 假如分类名不存在,再应用update()更新本条数据

    // 分类信息修正保留
    router.post('/category/edit', function (req, res, next) {
    // 猎取要修正的分类信息
    var id = req.query.id;
    // 猎取post要求提交的分类称号数据
    var nameCategory = req.body.category;
    // 检察提交的分类称号数据是不是存在
    Category.findOne({
      _id: id
    }).then(function (category) {
      if(!category) {
    res.render('admin/error', {
      userInfo: req.userInfo,
      message: "分类信息不存在"
    });
    return Promise.reject();
      } else {
      // 推断用户是不是做了修正
      if(nameCategory === category.name) {  // 假如没有修正,直接提醒修正胜利,跳转到首页
        res.render('admin/success', {
          userInfo: req.userInfo,
          message: "修正胜利",
          url: '/admin/category'
        });
        return Promise.reject();
      } else {
        // 推断增添的分类称号是不是已存在
        Category.findOne({
          id: {$ne: id},    // 差别的纪录中是不是存在雷同的分类称号
          name: nameCategory
        }).then(function (sameCategory) {
          if(sameCategory) {
            res.render('admin/error', {
              userInfo: req.userInfo,
              message: "分类称号已存在"
            });
            return Promise.reject();
          } else {    // 假如不反复,则保留改的数据
            Category.update({_id: id}, {name: nameCategory}).then(function () {
              res.render('admin/success', {
                userInfo: req.userInfo,
                message: "修正胜利",
                url: '/admin/category'
              });
            })
          }
        })
      }
      }
    })
    });
  6. 数据库的删除只需运用remove({_id: id})即可

    router.get('/category/delete', function (req, res, next) {
    // 猎取要删除分类的id
    var id = req.query.id;
    Category.remove({_id: id}).then(function () {
      res.render('admin/success', {
    userInfo: req.userInfo,
    message: "删除胜利",
    url: '/admin/category'
      });
    })
    });

2.6 文章治理

文章治理的完成逻辑与分类治理基础一致:

  1. 完成文章治理的列表展示页,有增添文章的进口

    // 文章首页
    router.get('/article', function (req, res, next) {
    
    // 从数据库猎取文章的内容
    var page = req.query.page || 1;
    var limit = 10;
    var pages = 0;
    
    // 分类治理时,基本知识应当讲新增添的分类放在最前面,所以展示时应当降序从数据库中读取数据
    Article.count().then(function (count) {
      pages = Math.ceil(count / limit);
      page = Math.min(page, pages);
      page = Math.max(1, page);
    
      var skip = (page - 1) * limit;
      // sort()束缚有两个值:1示意升序;-1示意降序
      Article.find().sort({_id: -1}).limit(limit).skip(skip).populate(['category', 'user']).then(function (articles) {
    console.log(articles);
    res.render('admin/article_index', {
      userInfo: req.userInfo,
      articles: articles,
      pages: pages,
      count: count,
      limit: limit,
      page: page
    });
      });
    });
    });
  2. 完成文章增添页的表单,经由过程post提交填写的数据

    router.get('/article/add', function (req, res, next) {
    // 从服务器中读取一切分类信息
    Category.find().sort({_id:-1}).then(function (categories) {
      res.render('admin/article_add', {
    userInfo: req.userInfo,
    categories: categories
      });
    });
    });
  3. 后端剖析猎取文章的数据,举行基础考证,经由过程后举行数据保留

    // 文章内容保留路由
    router.post('/article/add', function (req, res, next) {
    var categoryId = req.body.category;
    var description = req.body.description;
    var title = req.body.title;
    var article = req.body.article;
    
    // console.log(categoryId, description, title, article);
    // 基础考证,分类、题目、简介、内容不能为空
    if(!categoryId) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章分类不能为空"
      });
      return;
    }
    if(!title) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章题目不能为空"
      });
      return;
    }
    if(!description) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章简介不能为空"
      });
      return;
    }
    if(!article) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章内容不能为空"
      });
      return;
    }
    
    // 保留数据到数据库
    return new Article({    // 应用Model建立一个实例对象,应用save()要领保留数据
      category: categoryId,
      user: req.userInfo._id.toString(),
      title: title,
      description: description,
      article: article
    }).save().then(function () {
      res.render('admin/success', {
    userInfo: req.userInfo,
    message: "文章增添胜利",
    url: '/admin/article'
      });
    })
    });
  4. 文章的修正有两个步骤:首先是猎取文章的编辑页,将本来的内容衬着到页面上,编辑修正后,将数据提交到背景;背景完成基础考证后,更新数据库中的内容

    // 文章内容修正的数据提交
    router.post('/article/edit', function (req, res, next) {
    // 猎取文章的id
    var id = req.query.id;
    var category = req.body.category;
    var title = req.body.title;
    var description = req.body.description;
    var article = req.body.article;
    
    if(!category) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章分类不能为空"
      });
      return;
    }
    if(!title) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章题目不能为空"
      });
      return;
    }
    if(!description) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章简介不能为空"
      });
      return;
    }
    if(!article) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: "文章内容不能为空"
      });
      return;
    }
    
    Article.update({_id: id}, {
      category: category,
      description: description,
      title: title,
      article: article
    }).then(function () {
      res.render('admin/success', {
    userInfo: req.userInfo,
    message: '内容修正胜利',
    url: '/admin/article'
      })
    });
    });
  5. 文章的删除与分类删除一致:运用remove()要领

    router.get('/article/delete', function (req, res, next) {
    var id = req.query.id || '';
    if(!id) {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: '指定 文章不存在'
      })
      return;
    }
    Article.remove({_id: id}).then(function () {
      res.render('admin/error', {
    userInfo: req.userInfo,
    message: '删除胜利',
    url: '/admin/article'
      });
    })
    });

2.7 批评的治理

  1. 批评能够零丁存放在一个coolection中,便于种种操纵治理,通用性强,能够编辑和删除;增添复杂度

  2. 将批评存储为文章的一个字段,与一片文章绑定在一同,操纵轻便,然则编辑与删除功用很难完成

3 前台展示

经由过程后端将数据返回以后,服务器端平常将数据组织为JSON花样,便于操纵,能够应用后端模板或许前端操纵DOM的体式格局将数据增添到页面。

  • 前端衬着:能够减轻服务器端的开支,然则首屏的衬着会加长时候

  • 后端衬着:增添服务器的开支,然则削减客户端展示的时候

    原文作者:Kyxy
    原文地址: https://segmentfault.com/a/1190000008685702
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞