项目开始

  • 项目 主要由 koa2 + sequelize + mysql 简单博客项目
  • koa2 koa-pure-blog 生成项目
{
  "name": "koa-pure-blog",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "start": "node bin/www",
    "dev": "cross-env NODE_ENV=dev ./node_modules/.bin/nodemon  bin/www",
    "prd": "pm2 start bin/www",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "debug": "^4.1.1",
    "koa": "^2.7.0",
    "koa-bodyparser": "^4.2.1",
    "koa-convert": "^1.2.0",
    "koa-generic-session": "^2.3.0",
    "koa-json": "^2.0.2",
    "koa-logger": "^3.2.0",
    "koa-onerror": "^4.1.0",
    "koa-redis": "^4.0.1",
    "koa-router": "^7.4.0",
    "koa-static": "^5.0.0",
    "koa-views": "^6.2.0",
    "mysql2": "^2.3.3",
    "pug": "^2.0.3",
    "sequelize": "^6.25.8"
  },
  "devDependencies": {
    "cross-env": "^7.0.3",
    "nodemon": "^1.19.1"
  }
}

添加用户和博客路由

  • 用户路由只有登录
  • 博客路由有增删改查
const router = require('koa-router')()
const { login } = require('../controller/user')
router.prefix('/api/user')
const { SuccessModel, ErrorModel } = require('../model/resModel')

// 登录
router.post('/login', async (ctx, next) => {
  const { username, password } = ctx.request.body
  const data = await login(username, password)
  if (data.username) {
    // 设置 session
    ctx.session.username = data.username
    ctx.session.realname = data.realname
    ctx.body = new SuccessModel()
    return
  }
  ctx.body = new ErrorModel('登录失败')
})

module.exports = router
const router = require('koa-router')()
const {
  getList,
  getDetail,
  newBlog,
  updateBlog,
  delBlog,
} = require('../controller/blog')
router.prefix('/api/blog')
const { SuccessModel, ErrorModel } = require('../model/resModel')

// 登录中间件
const checkLogin = require('../middleware/checkLogin')

// 博客列表
router.get('/list', async (ctx, next) => {
  const author = ctx.query.author || ''
  const keyword = ctx.query.keyword || ''
  const listData = await getList(author, keyword)
  ctx.body = new SuccessModel(listData)
})

// 博客详情
router.get('/detail', async (ctx, next) => {
  const data = await getDetail(ctx.query.id)
  ctx.body = new SuccessModel(data)
})

// 新建一篇博客
router.post('/new', checkLogin, async (ctx, next) => {
  const author = ctx.session.username
  ctx.request.body.author = author
  const data = await newBlog(ctx.request.body)
  ctx.body = new SuccessModel(data)
})

// 更新一篇博客
router.post('/update', checkLogin, async (ctx, next) => {
  const val = await updateBlog(ctx.query.id, ctx.request.body)
  if (val) {
    ctx.body = new SuccessModel()
  } else {
    ctx.body = new ErrorModel('更新博客失败')
  }
})

// 删除一篇博客
router.post('/del', async (ctx, next) => {
  const author = ctx.session.username
  const val = await delBlog(ctx.query.id, author)
  if (val) {
    ctx.body = new SuccessModel()
  } else {
    ctx.body = new ErrorModel('删除博客失败')
  }
})

module.exports = router
class BaseModel {
  constructor(data, message) {
    if (typeof data === 'string') {
      this.message = data
      data = null
      message = null
    }
    if (data) {
      this.data = data
    }
    if (message) {
      this.message = message
    }
  }
}

class SuccessModel extends BaseModel {
  constructor(data, message) {
    super(data, message)
    this.errno = 0
  }
}

class ErrorModel extends BaseModel {
  constructor(data, message) {
    super(data, message)
    this.errno = -1
  }
}

module.exports = {
  SuccessModel,
  ErrorModel,
}

添加用户和博客控制器

const User = require('../db/model/User')
const { SuccessModel, ErrorModel } = require('../model/ResModel')
// 用户登录
const login = async (username, password) => {
  const data = await User.findOne({
    where: {
      username,
      password,
    },
  })
  if (data === null) return null
  return data.dataValues
}

module.exports = {
  login,
}
const Sequelize = require('sequelize')
const Blog = require('../db/model/Blog')
const { SuccessModel, ErrorModel } = require('../model/resModel')

// 获取博客列表
const getList = async (author = '', keyword = '') => {
  const whereOpt = {}
  if (author) {
    whereOpt.author = author
  }
  if (keyword) {
    whereOpt.title = {
      [Sequelize.Op.like]: `%${keyword}%`,
    }
  }
  const list = await Blog.findAll({
    where: whereOpt,
    order: [['id', 'desc']],
  })

  return list.map((item) => item.dataValues)
}

// 获取博客详情
const getDetail = async (id) => {
  const detail = await Blog.findOne({
    where: {
      id,
    },
  })
  return detail.dataValues
}

// 新建一篇博客
const newBlog = async (blogData = {}) => {
  const { title, content, author } = blogData
  const result = await Blog.create({
    title,
    content,
    author,
  })
  return result.dataValues
}

// 更新一篇博客
const updateBlog = async (id, blogData = {}) => {
  const { title, content } = blogData
  const result = await Blog.update(
    {
      title,
      content,
    },
    {
      where: {
        id,
      },
    }
  )
  return result[0] > 0
}

// 删除一篇博客
const delBlog = async (id, author) => {
  const result = await Blog.destroy({
    where: {
      id,
      author,
    },
  })
  return result > 0
}

module.exports = {
  getList,
  getDetail,
  newBlog,
  updateBlog,
  delBlog,
}

连接数据库建立表

  • 安装 sequelizemysql2
const Sequelize = require('sequelize')
const { MYSQL_CONF } = require('../config/db')
// 配置
const conf = {
  host: MYSQL_CONF.host,
  dialect: 'mysql',
}

// 线上环境,使用连接池
if (process.env.NODE_ENV === 'production') {
  conf.pool = {
    max: 5, // 连接池中最大的连接数量
    min: 0, // 最小
    idle: 10000, // 如果一个连接池 10s 之内没有被使用,则释放
  }
}

// 创建连接
const seq = new Sequelize(
  MYSQL_CONF.database,
  MYSQL_CONF.user,
  MYSQL_CONF.password,
  conf
)

module.exports = seq
const Sequelize = require('sequelize')
const seq = require('../seq')

// 创建 User 模型
const User = seq.define('pure_user', {
  // id 会自动创建,并设为主键,自增
  //
  username: {
    type: Sequelize.STRING, // varchar(255)
    allowNull: false,
  },
  password: {
    type: Sequelize.STRING,
    allowNull: false,
  },
  realname: {
    type: Sequelize.STRING,
  },
})

module.exports = User
const Sequelize = require('sequelize')
const seq = require('../seq')

// 创建 Blog 模型
const Blog = seq.define('pure_blog', {
  // id 会自动创建,并设为主键,自增
  //
  title: {
    type: Sequelize.STRING, // varchar(255)
    allowNull: false,
  },
  content: {
    type: Sequelize.TEXT, // longtext
    allowNull: false,
  },
  author: {
    type: Sequelize.STRING,
    allowNull: false,
  },
})

module.exports = Blog
const seq = require('./seq')

// 需要同步的模型列表
require('./model/Blog')
require('./model/User')

// 测试连接
seq
  .authenticate()
  .then(() => {
    console.log('ok')
  })
  .catch(() => {
    console.log('err')
  })

// 执行同步
seq.sync({ force: true }).then(() => {
  console.log('sync ok')
  process.exit()
})

使用 redis 保存 session

  • 安装 koa-generic-sessionkoa-redis
const session = require('koa-generic-session')
const redisStore = require('koa-redis')
const { REDIS_CONF } = require('./config/db')

...

// session 配置
app.keys = ['WJiol#231_']
app.use(
  session({
    key: 'w23423423',
    prefix: 'weibo:sess:',
    // 配置 cookie
    cookie: {
      path: '/',
      httpOnly: true,
      maxAge: 24 * 60 * 60 * 1000,
    },
    // 配置 redis
    store: redisStore({
      all: `${REDIS_CONF.host}:${REDIS_CONF.port}`,
    }),
  })
)

// 路由注册之前

接口测试工具

### 变量

@BASE_URL = http://localhost:3000/api

### 测试
GET http://localhost:3000/ HTTP/1.1


### 博客列表
GET {{BASE_URL}}/blog/list HTTP/1.1

### 博客详情
GET {{BASE_URL}}/blog/detail?id=1 HTTP/1.1


### 新建博客
POST {{BASE_URL}}/blog/new HTTP/1.1
Content-Type: application/json

{
    "title": "测试标题 koa sequelize",
    "content": "测试内容3324koa sequelize",
    "createtime": 1560000000000
}

### 修改博客
POST {{BASE_URL}}/blog/update?id=1 HTTP/1.1
Content-Type: application/json

{
    "title": "测试标题koa",
    "content": "测试内容koa",
    "author": "admin"
}

### 删除博客
POST {{BASE_URL}}/blog/delete?id=1


### 用户登录
POST {{BASE_URL}}/user/login
Content-Type: application/json

{
  "username": "admin",
  "password": "admin"
}


### 用户注册
POST {{BASE_URL}}/user/register
Content-Type: application/json

{
  "username": "admin",
  "password": "admin"
}