1 Star 0 Fork 0

寒江雪 / site-api

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
README.md 9.80 KB
一键复制 编辑 原始数据 按行查看 历史
寒江雪 提交于 2020-01-09 14:24 . 产品分页数据

vue企业站的接口

初始化接口项目

  1. npm init -y
  2. cnpm i koa koa-router koa-bodyparser -S
  3. cnpm i nodemon -D
  4. package.json下增加开发启动脚本
"scripts": {
    "dev": "nodemon app.js"
  },

开发调试环境

  1. 为了方便在VSCode编辑器下调试,首先把nodemon安装在全局
  2. 点击VSCode编辑器的调试小虫子,添加Node.js nodemon等几个调试配置,生成的.vscode/launch.json文件内容如下:
{
  // 使用 IntelliSense 了解相关属性。 
  // 悬停以查看现有属性的描述。
  // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "nodemon",
      "runtimeExecutable": "nodemon",
      "program": "${workspaceFolder}/app.js",
      "restart": true,
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "启动程序",
      "program": "${workspaceFolder}\\app.js"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "当前文件",
      "program": "${file}"
    }    
  ]
}
  1. 为了方便windows环境下修改NODE_ENV的值,安装 cross-env: cnpm i cross-env -D

路由

  1. 安装 require-directory: cnpm i require-directory -S
  2. 编写路由 app/router/home.js
const Router = require('koa-router')

const router = new Router()

router.get('/', async(ctx, next) => {
  ctx.body = `<h1>我是首页</h1>`
})

router.get('/siteinfo', async(ctx, next) => {
  ctx.body = {
    siteName: '站点名称',
    siteUrl: 'http://www.everydaykaixin.com',
    title: 'xxxxx',
    description: '站点描述',
    footer: 'Copyright @ 2020 www.wxboyan.com. 无锡博言传动机械有限公司版权所有 苏ICP备16021376'
  }
})

// module.exports = { router } // 这种导出路由的方式也支持
module.exports = router
  1. 注册路由
const Koa = require('koa')
const Router = require('koa-router')
const requireDirectory = require('require-directory')

const app = new Koa()

requireDirectory(module, './app/router', { visit: whenModuleLoaded })

function whenModuleLoaded(obj) {
  if(obj instanceof Router) {
    app.use(obj.routes()).use(obj.allowedMethods())
  } else {
    let objKeys = Object.keys(obj)
    if(!objKeys.length) return
    for(let key of objKeys) {
      const router = obj[key]
      if(router instanceof Router) { // 找到模块导出的是Router,就注册为路由,默认只为模块注册一个路由,其它导出忽略
        app.use(router.routes()).use(router.allowedMethods())
        break
      }
    }
  }
}

app.listen(8888, () => {
  console.log(`服务运行于端口8888`)
})

异常处理

  1. 定义异常类HttpException,该类继承自Error类,以便throw, 并定义一些继承自HttpException的子类
class HttpException extends Error {
  constructor(msg = '服务器异常', errorCode = 10000, code = 400) {
    super()
    this.errorCode = errorCode
    this.code = code
    this.msg = msg
  }
}

class ParameterException extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 400
    this.msg = msg || '参数错误'
    this.errorCode = errorCode || 10000
  }
}

class Success extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 201
    this.msg = msg || 'ok'
    this.errorCode = errorCode || 0
  }
}

class NotFound extends HttpException{
  constructor(msg, errorCode) {
      super()
      this.msg = msg || '资源未找到'
      this.errorCode = errorCode || 10000
      this.code = 404
  }
}

class AuthFailed  extends HttpException {
  constructor(msg, errorCode) {
      super()
      this.msg = msg || '授权失败'
      this.errorCode = errorCode || 10004
      this.code = 401
  }
}

module.exports = {
  HttpException,
  ParameterException,
  Success,
  NotFound,
  AuthFailed
}
  1. 全局异常捕获中间件 exception.js
const { HttpException } = require('../core/http-exception')

const catchError = async (ctx, next) => {
  try {
    await next()
  } catch (error) {
    if(error instanceof HttpException) { // 处理自己定义的异常
      ctx.body = {
        msg: error.msg,
        errorCode: error.errorCode,
        request: `${ctx.method} ${ctx.request.path}`
      }
      ctx.status = error.code
      return
    }
    ctx.body = {
      msg: '抱歉,服务器发生了异常',
      errorCode: 999,
      request: `${ctx.method} ${ctx.request.path}`
    }
    ctx.status = 500
  }
}

module.exports = catchError

参数检验

  1. 安装lin-validator的依赖包
  • yarn add validator lodash

数据库

  1. 新建MySQL数据库

建数据库

  1. 安装sequelize
  • yarn add sequelize
  • sequelize官方文档
  • 因为用的mysql数据库,安装mysql2数据库驱动:yarn add mysql2
  • 数据库连接
const Sequelize = require('sequelize')
const { database } = require('../config/config')

const { dbName, host, port, user, password } = database

const sequelize = new Sequelize(dbName, user, password, {
  host,
  port,
  dialect: 'mysql',
  // dialect: /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
  logging: true,
  dialectOptions: {
    dateStrings: true, // mysql获取时间时会自动作时区转换,禁止mysql的转换
    typeCast(field, next) { // 覆盖了sequelize的转换,看代码,目前只影响date和GEOMETRY,所以加上DATETIME类型
      if(field.type === 'DATETIME') {
        return field.string()
      }
      return next()
    }
  },
  timezone: '+08:00',  
  define: {
    timestamps: true, // 会自动生成 createAt和updateAt
    freezeTableName: true  // 不将表格名称复数化
  }
})

sequelize.sync()

// Note: using `force: true` will drop the table if it already exists
// sequelize.sync({force: true})

sequelize.authenticate().then(() => {
  console.log('数据库连接成功.')
}).catch(err => {
  console.error('数据库连接失败:', err)
})

module.exports = { sequelize }
  • 定义模型
const { Sequelize, Model } = require('sequelize')
const { sequelize } = require('../../core/db')

class User extends Model {}

User.init({
  id: {
    type: Sequelize.INTEGER,
    autoIncrement: true,
    primaryKey: true
  },
  userName: {
    type: Sequelize.STRING(30),
    unique: true
  },
  password: Sequelize.STRING(50),
  loginCount: {
    type: Sequelize.INTEGER,
    defaultValue: 0
  }
}, {
  sequelize,
  tableName: 'User',
  paranoid: true  // 会自动增加 deleteAt字段,实行软删除
})

module.exports = {
  User
}
  • 定义验证器 app/validators/userValidator.js
const { LinValidator, Rule } = require('../../core/lin-validator-v2')

class RegisterValidator extends LinValidator {
  constructor() {
    super()
    // 注册时id不用提供
    // this.id = [
    //   new Rule('isInt', 'id必须是正整数', {min: 1})
    // ]
    this.userName = [
      new Rule('isLength', '用户名必须是2-32个字符', {
          min: 2,
          max: 32
      })
    ]
    this.password = [
      new Rule('isLength', '密码至少6个字符,最多32个字符', {
        min: 6,
        max: 32
      }),
      new Rule('matches', '密码不符合规范', '^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]')
    ]
    this.confirmPassword = this.password
  }

  validatePassword(vals) { // 自定义的验证器必须以validate开头
    const psw1 = vals.body.password
    const psw2 = vals.body.confirmPassword
    if (psw1 !== psw2) {
      throw new Error('密码和确认密码必须相同')
    }
  }
}

module.exports = {
  RegisterValidator
}
  • 处理请求
const Router = require('koa-router')
const { success } = require('../../lib/helper')
const { RegisterValidator } = require('../../validators/userValidator')
const { User } = require('../../models/User')

const router = new Router({prefix: '/admin/user'})

router.post('/register', async(ctx) => {
  const v = await new RegisterValidator().validate(ctx)
  const user = {
    userName: v.get('body.userName'),
    password: v.get('body.password')
  }
  await User.create(user)
  success('注册成功')
})

// module.exports = { router } // 这种导出路由的方式也支持
module.exports = router

处理api返回的图片路径问题

  1. 安装 koa-static中间件: yarn add koa-static
  2. app.js配置上中间件
const path = require('path')
const static = require('koa-static')

app.use(static(path.join(__dirname,'./static')))

分页数据获取

/**
 * 获取产品分页数据
*/
router.post('/pageproduct/:id?', async(ctx) => {
  const v = await new ProductQueryValidator().validate(ctx)
  const pageIndex = v.get('body.pageIndex')
  const pageSize = v.get('body.pageSize')

  const cateId = v.get('path.id')
  let where = null
  if(cateId) { // 如果有传入分类id, 表明是查询该分类下的分页数据,默认是所有产品的分页数据
    where = {
      ProCateId: cateId * 1
    }
  }

  const offset = (pageIndex - 1) * pageSize

  const products = await Product.findAndCountAll({
    where: where, 
    attributes: { exclude: ['createdAt','updatedAt'] },
    include: [
      { model: ProCate, required: true, attributes: { exclude: ['content','createdAt','updatedAt'] } }
    ],
    order: [
      ['orderNum', 'DESC']
    ],
    offset: offset,
    limit: pageSize
  })
  const httpOrigin = ctx.origin
  products.rows.forEach(item => item.pic = httpOrigin + item.pic)
  ctx.body = new SuccessResult(products)
})

上面的查询要注意在定义模型时 定义好关联关系

ProCate.hasMany(Product, {
  constraints: false
})
Product.belongsTo(ProCate, { // 会给Product表自动生成 ProCateId (模型名ProCate + id  并且是驼峰形式所以是 ProCateId)
  constraints: false
})
JavaScript
1
https://gitee.com/jxmlearner/site-api.git
git@gitee.com:jxmlearner/site-api.git
jxmlearner
site-api
site-api
master

搜索帮助