Koa+mySql

844 阅读2分钟

创建项目

Koa中文网

  1. 安装Koa脚手架
npm install -g koa-generator
  1. 生成项目
koa -e koa2_learn or  koa koa2_learn

配置中间件

  • 配置cors 跨域中间件
npm i koa2-cors

const cors = require('koa2-cors')
app.use(cors())
  • 配置swagger装饰的路由
npm install koa2-swagger-ui
npm install swagger-jsdoc  

新建utils/swagger.js


const router = require('koa-router')() //引入路由函数
const path = require('path')

const swaggerJSDoc = require('swagger-jsdoc')

const swaggerDefinition = {
  info: {
    title: 'koa服务端API',
    version: '1.0.0',
    description: 'koa服务端API接口文档'
  },
  host: 'localhost:3000', // 接口文档访问地址为:localhost:3000/swagger
  basePath: '/' // Base path (optional)
}
const options = {
  swaggerDefinition,
  apis: [path.join(__dirname, './routes/*.js')] // 写有注解的router的存放地址, 最好path.join()
}
const swaggerSpec = swaggerJSDoc(options)
// 通过路由获取生成的注解文件
router.get('/swagger.json', async function (ctx) {
  ctx.set('Content-Type', 'application/json')
  ctx.body = swaggerSpec
})
module.exports = router
  • 在app.js注册KoaSwagger中间件

const { koaSwagger } = require('koa2-swagger-ui')
// 引入总路由
const routes = require('./routes')

注册中间件

  .use(
    koaSwagger({
      routePrefix: '/swagger', // 接口文档访问地址
      swaggerOptions: {
        url: '/swagger.json' // example path to json 其实就是之后swagger-jsdoc生成的文档地址
      }
    })



routes(app) // 挂载路由

路由模块

  • routrs/index.js 自动导入所有子路由模块
/*
*所有的路由接口
*/
const fs = require('fs')
module.exports = (app)=>{
  fs.readdirSync(__dirname).forEach(file=>{
    if(file === 'index.js'){
      return
    }
    const router = require(`./${file}`)
    app.use(router.routes()).use(router.allowedMethods())
  })
}

  • routes/users.js

// const Router = require('koa-router')
// const router = new Router()

// 引入经过 swagger 装饰的 router
const router = require('../utils/swagger')
// 设置统一前缀
router.prefix('user')

//用户相关的处理函数
const {
  login,
  register,
  getUserInfo,
  updateUserInfo,
  updatePassword,
  updateAvatar
} = require('../controller/users')

//数据验证
const koaJoi = require('../validator')

const {
  reg_login_schema,
  update_userinfo_schema,
  update_password_schema,
  update_avatar_schema
} = require('../validator/user')

//用户登录
router.post('/login', koaJoi('post', reg_login_schema), login)

//用户注册
router.post('/reguser', koaJoi('post', reg_login_schema), register)

//获取用户信息
router.get('/userinfo', getUserInfo)

//更新用户的基本信息
router.post('/userinfo', updateUserInfo)
// 重置密码
router.post('/updatepwd', updatePassword)

// 更新用户头像
router.post('/update/avatar', updateAvatar)

module.exports = router

安装连接mysql数据库

npm install --save mysql
  • 新建db/index.js

const mysql = require('mysql')

function connect() {
  return mysql.createConnection({
    host: '127.0.0.1',
    user: 'zoe',
    password: '123456',
    database: 'zoe',
    connectionLimit: 5
  })
}

const sqlconnection = (sql, parmas = null) => {
  // 获取数据库链接对象
  const conn = connect()
  return new Promise(function (resolve, reject) {
    // 执行SQL语句
    try {
      conn.query(sql, parmas, (err, results) => {
        if (err) {
          reject(err)
        } else {
          resolve(results)
        }
      })
    } catch (e) {
      reject(e)
    } finally {
      conn.end() // 关闭链接
    }
  })
}

module.exports = { sqlconnection }

用户注册API

 // 用于密码加密
npm install --save bcryptjs
const db = require('../db')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const { jwtSecret, expiresIn } = require('../config')

const CONSTANT = require('../config/constant')

//用户注册
exports.register = async (ctx) => {
  let req = ctx.request
  //获取请求体数据
  const userinfo = req.body

  const sqlStr = `select * from users where username=?`

  const results = await db.sqlconnection(sqlStr, [userinfo.username])
  // 用户名被占用
  if (results.length > 0)
    return responseClient(ctx, RES_CODE.dataFail, '用户名已存在,请更换其他用户名')

  // 密码进行加密处理
  userinfo.password = bcrypt.hashSync(userinfo.password, 10)

  // console.log('加密后', userinfo)

  // 插入用户
  const sql = 'insert into users set ?'
  // 验证通过 将数据保存到数据库
  const res = await db.sqlconnection(sql, {
    username: userinfo.username,
    password: userinfo.password
  })
  // sql语句执行成功,但影响行数不为1
  if (res.affectedRows !== 1) {
    return responseClient(ctx, RES_CODE.dataFail, '注册用户失败,请稍后再试!')
  }

  responseClient(ctx, RES_CODE.reqSuccess, '注册成功')
}

 //  responseClient 该方法为封装的统一返回方法

用户登录API

// 用于token
npm i jsonwebtoken koa-jwt 
  • 在config/index.js 下配置 token 密钥和有效期

// 秘钥取自 UUID

module.exports = {
  // token 秘钥
  jwtSecret: '860450f9f4ff1ad59019f7ff4909021a',
  //token 有效期
  expiresIn: '10h',
  port: 3000
}
//用户登录
exports.login = async (ctx) => {
  let req = ctx.request
  const userinfo = req.body

  // console.log('userinfo', userinfo)

  // 根据用户名查询用户数据
  const sqlStr = `select * from users where username=?`
  const results = await db.sqlconnection(sqlStr, userinfo.username)

  if (results.length !== 1) return responseClient(ctx, RES_CODE.dataFail, '登录失败')

  // 密码对比
  const compareResult = bcrypt.compareSync(
    userinfo.password, //用户提交的密码
    results[0].password //数据库中的密码
  )
  if (!compareResult) return responseClient(ctx, RES_CODE.dataFail, '用户名或密码错误')

  const user = { ...results[0], password: '', user_pic: '' }
  console.log('user', user)

  const tokenStr = jwt.sign(user, jwtSecret, { expiresIn: expiresIn })

  let data = {
    user: user,
    token: `Bearer ${tokenStr}`
  }
  responseClient(ctx, RES_CODE.reqSuccess, '登录成功', data)
}

注册,登录时字段验证

npm install joi

  1. 新建validator/index.js 封装验证方法函数
function schema(method, schemas) {
  async function validateSchema(ctx, next) {
    let data = null
    if (method === 'get') {
      data = await ctx.request.query
    } else {
      data = await ctx.request.body
    }

    const { error } = schemas.validate(data)
    if (error) {
      ctx.body = error.message
      // severErr(error)
      return
    }
    await next()
  }
  return validateSchema
}
module.exports = schema
  1. 在validator/user.js 统一管理用户相关的验证字段
const joi = require('joi')
/**
 *string()值必须是字符串
 *alphanum()值只能是包含a-zA-Z0-9的字符串
 *min(length)最小长度*max(length)最大长度
 *required()值是必填项,不能为undefined
 *pattern(正则表达式)值必须符合正则表达式的规则
 * */
//用户名的验证规则
const username = joi.string().alphanum().min(1).max(10).required()
//密码的验证规则
const password = joi
  .string()
  .pattern(/^[\S]{6,12}$/)
  .required()

//定义id,nickname,emial的验证规则
const id = joi.number().integer().min(1).required()
const nickname = joi.string().required()
const email = joi.string().email().required()
//dataUri()指的是如下格式的字符串数据:
//data:image/png;base64,VE9PTUFOWVNFQ1JFVFM=
const avatar = joi.string().dataUri().required()


// 比如注册登入只需验证用户名和密码这两个字段
exports.reg_login_schema = joi.object({
  username,
  password
})

// 更新用户基本信息
exports.update_userinfo_schema = joi.object{{
    id,
    nickname,
    email
  }}
  1. 在路由users模块使用
//数据验证
const koaJoi = require('../validator')

//用户登录
router.post('/login', koaJoi('post', reg_login_schema), login)

//用户注册
router.post('/reguser', koaJoi('post', reg_login_schema), register)

路由鉴权

  1. 新建utils/jwt.js 配置路由权限中间件实现对非登录和注册API需要Token认证
//解析token的中间件
// const expressJwt = require('express-jwt')

const koaJwt = require('koa-jwt')
const { jwtSecret } = require('../config/index')

module.exports = koaJwt({
  secret: jwtSecret, //携带私钥
  algorithms: ['HS256'], // 默认值
  credentialsRequired: true // 设置为false就不进行校验了,游客也可以访问
}).unless({
  path: [
    // '/',
    '/login',
    '/reguser'
  ] // 设置 jwt 认证白名单
})
  1. 在app.js注册jwt中间件
//解析token的中间件
const jwtAuth = require('./utils/jwt')

// 挂载在路由之前
app.use(jwtAuth)