egg 做一个超级简单的博客网站

egg 文档 https://eggjs.org/zh-cn/intro/index.html
Nunjucks https://adonis-china.org/docs/3.2/templating
Mongoose 5.0 https://mongoosedoc.top/docs/index.html

本项目 github : https://github.com/guxiansheng1991/blog-egg.git

一.目标

学习阿里开源的egg这个node框架,熟悉node的相关技术

二.项目搭建及功能

(1) 搭建使用egg-init官方脚手架搭建的,简单方便

(2) 实现功能

  • 博客crud
  • 支持博客分类
  • 登录注册
  • 评论功能可选(还没做)
  • 点赞功能可选(还没做)

(3) 使用技术

  • 框架使用egg
  • 数据库 MongoDB
  • 模板view egg-view-nunjucks

三.项目开发

(1) 配置

配置统一在/root_path/config/config.default.js中
配置文件中可以配置模板文件,数据库,session,中间件等等,比较丰富

'use strict';

const path = require('path');

module.exports = appInfo => {
  const config = exports = {};

  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1534300430738_2489';

  // add your config here
  config.middleware = [ 'authentication' ];
  // config.middleware = [];

  config.authentication = {
    excepts: [ '/user/login' ],
  };

  // view模板配置
  config.view = {
    // 配置view目录,可以多个,只需要在root数组中多个即可
    root: [
      path.join(appInfo.baseDir, 'app/view'),
    ].join(','),
    // 配置模板引擎的后缀信息
    mapping: {
      '.nj': 'nunjucks',
    },
    // 默认的模板引擎
    defaultViewEngine: 'nunjucks',
    // 默认的view文件渲染后缀,配置此项可以在render函数中省略后缀名
    defaultExtension: '.nj',
  };

  // MongoDB配置
  config.mongoose = {
    client: {
      url: 'mongodb://127.0.0.1/blogegg',
      options: {},
    },
  };

  // session设置
  config.session = {
    renew: true,
  };

  return config;
};

(2) 数据库设计

数据库使用MongoDB,设计文件如下
以Blog为例, 这里将文档之间的对应关系采用引用存储,类似于Mysql的外键

// /root_path/app/model/blog.js
'use strict';

module.exports = app => {
  const mongoose = app.mongoose;
  const Schema = mongoose.Schema;
  const BlogSchema = new Schema({
    title: { type: String },
    content: { type: String },
    category: { type: String },
    userId: { type: String },
  });
  return mongoose.model('Blog', BlogSchema);
};

(2) 鉴权

需要使用中间件,我是自己定义的中间件,配置如下:

鉴权行为是放在 await next(); 后,不知道对还是不对,确实有效

// /root_path/config/config.default.js
// add your config here
  config.middleware = [ 'authentication' ];
  config.authentication = {
    excepts: [ '/user/login' ],
  };
// /root_path/app/middleware/authentication.js
'use strict';

module.exports = options => {
  return async function authentication(ctx, next) {
    await next();
    const user = ctx.session.user;
    let res = false;
    if (!user) {
      const routeStr = ctx.request.url;
      for (let i = 0; i < options.excepts.length; i++) {
        if (options.excepts[i] === routeStr) {
          res = true;
          break;
        }
      }
    } else {
      res = true;
    }
    if (!res) {
      ctx.redirect('/user/login');
    }
  };
};

(3) crud

这里以Blog为例
这里程序主要采用类似于java的MVC架构, 类似于Spring MVC,
controller 调用 service
service 调用 dao(这里的dao就是利用mongoose操作MongoDB)

路由文件(遵循RESTful 风格)

// /root_path/app/router.js
'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller } = app;
  router.get('/blog/list/:page', controller.blog.list);
  router.get('/blog/add', controller.blog.add);
  router.post('/blog/addAction', controller.blog.addAction);
  router.get('/blog/detail/:id', controller.blog.detail);
  router.get('/blog/edit/:id/:currentPage', controller.blog.edit);
  router.post('/blog/editAction/:currentPage', controller.blog.editAction);
  router.get('/blog/delete/:id/:currentPage', controller.blog.delete);
};

controller文件(这里对鉴定参数空值不知道怎么优雅的实现)

'use strict';

const Controller = require('egg').Controller;
// const verify = require('../util/verify');

class BlogController extends Controller {
  async list() {
    const page = this.ctx.params.page;
    const pageSize = 10;
    const res = await this.ctx.service.blog.list(page, pageSize);
    await this.ctx.render('blogs', res);
  }

  async add() {
    const data = {
      msg: '',
      title: '',
      content: '',
      categoryId: '4',
      categoryList: [],
    };
    const categoryList = await this.ctx.service.category.list();
    if (categoryList) {
      data.categoryList = categoryList;
    } else {
      const data = {
        msg: '获取类别列表失败, 请重试!',
      };
      await this.ctx.render('error', data);
    }
    await this.ctx.render('blogAdd', data);
  }

  async addAction() {
    const title = this.ctx.request.body.title;
    const content = this.ctx.request.body.content;
    const category = this.ctx.request.body.category;
    let msg = '';
    if (!title) {
      msg += 'title不能为空值, ';
    }
    if (!content) {
      msg += 'content不能为空值, ';
    }
    if (!category) {
      msg += 'category不能为空值, ';
    }
    if (!msg) {
      const res = await this.ctx.service.blog.add(title, content, category);
      if (res) {
        this.ctx.redirect('/blog/list/1');
      } else {
        const data = {
          msg: '新增失败, 请重试',
          title,
          content,
          category,
        };
        await this.ctx.render('/blog/add', data);
      }
    } else {
      const data = {
        message: msg,
        title,
        content,
        category,
      };
      await this.ctx.render('/blog/add', data);
    }
  }

  async edit() {
    const id = this.ctx.params.id;
    // const currentPage = this.ctx.params.currentPage;
    const res = await this.ctx.service.blog.findOne(id);
    const data = {
      msg: '',
      data: res,
      categoryList: [],
    };
    const categoryList = await this.ctx.service.category.list();
    if (categoryList) {
      data.categoryList = categoryList;
    } else {
      const data = {
        msg: '获取类别列表失败, 请重试!',
      };
      await this.ctx.render('error', data);
    }
    await this.ctx.render('blogEdit', data);
  }
  async editAction() {
    const id = this.ctx.request.body.id;
    const currentPage = this.ctx.params.currentPage;
    const update = {
      title: this.ctx.request.body.title,
      content: this.ctx.request.body.content,
      category: this.ctx.request.body.category,
    };
    const res = await this.ctx.service.blog.editAction(id, update);
    if (res) {
      this.ctx.redirect(`/blog/list/${currentPage}`);
    } else {
      this.ctx.redirect(`/blog/edit/${id}/${currentPage}`);
    }
  }

  async delete() {
    const id = this.ctx.params.id;
    const currentPage = this.ctx.params.currentPage;
    await this.ctx.service.blog.delete(id);
    this.ctx.redirect(`/blog/list/${currentPage}`);
  }

  async detail() {
    const id = this.ctx.params.id;
    const res = await this.ctx.service.blog.findOne(id);
    let category = null;
    const errorData = {
      msg: '',
    };
    if (res || res.category) {
      category = await this.ctx.service.category.findOne(res.category);
    } else {
      errorData.msg = '获取该博客信息失败,请重试!';
      await this.ctx.render('error', errorData);
    }
    const data = {
      msg: '',
      data: res,
      categoryName: '',
    };
    if (category) {
      data.categoryName = category.name;
    }
    await this.ctx.render('blogDetail', data);
  }
}

module.exports = BlogController;

service文件

'use strict';

const Service = require('egg').Service;

class BlogService extends Service {
  async list(page, pageSize) {
    const user = this.ctx.session.user;
    let data = null;
    try {
      const res = await Promise.all([
        this.ctx.model.Blog.find({
          userId: user._id,
        }).skip(pageSize * (page - 1)).limit(pageSize),
        this.ctx.model.Blog.find().count(),
      ]);
      data = {
        list: res[0],
        totalPage: Math.ceil(res[1] / pageSize),
        currentPage: page,
      };
    } catch (error) {
      data = null;
      console.error('blog list error', error);
    }
    return data;
  }

  async findOne(id) {
    const user = this.ctx.session.user;
    let data = null;
    try {
      const res = await this.ctx.model.Blog.findOne({
        _id: id,
        userId: user._id,
      });
      data = res;
    } catch (error) {
      data = null;
      console.error('blog findOne error', error);
    }
    return data;
  }

  async add(title, content, category) {
    let res = null;
    const user = this.ctx.session.user;
    try {
      res = this.ctx.model.Blog.create({
        title,
        content,
        category,
        userId: user._id,
      });
    } catch (error) {
      res = null;
      console.error('error, 创建失败', error);
    }
    return res;
  }

  async editAction(id, update) {
    let res = null;
    try {
      res = await this.ctx.model.Blog.findByIdAndUpdate(id, update);
    } catch (error) {
      res = null;
      console.error('error, 更新微博失败', error);
    }
    return res;
  }

  async delete(id) {
    let res = null;
    try {
      res = await this.ctx.model.Blog.remove({
        _id: id,
      });
    } catch (error) {
      res = {
        n: 0,
        ok: 0,
      };
      console.error('error, 删除失败', error);
    }
    return res;
  }
}

module.exports = BlogService;

四.总结

虽然做的很丑,但是主要目的是熟悉技术
还有很多需要做的,等待完善,例如数据库查询优化,三方登录,安全性等

本项目 github : https://github.com/guxiansheng1991/blog-egg.git

猜你喜欢

转载自blog.csdn.net/guxiansheng1991/article/details/81811262
egg