1 Star 0 Fork 0

寒江雪 / site-api

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

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
})
MIT License Copyright (c) 2020 寒江雪 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

vue-site的后端api 展开 收起
JavaScript
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
JavaScript
1
https://gitee.com/jxmlearner/site-api.git
git@gitee.com:jxmlearner/site-api.git
jxmlearner
site-api
site-api
master

搜索帮助