用egg.js实现路由以及前端接口调用

1,963 阅读5分钟

自己搭建了一个前后端的项目,基于目前项目组的开发框架,主要是实现了在node端如何生成一个接口,以及在前端项目如何调用

前端用的 vue + axios 后端用的 egg.js + sequelize

1. 建表

我用一个自己随便写的排行榜的表进行模拟,虽然是模拟,但是也要加上注释哦

CREATE TABLE `rank_list` (
        `id` INT(11) NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '排行内容' COLLATE 'utf8mb4_general_ci',
        `created_at` INT(11) NOT NULL COMMENT '创建时间,unix时间戳,单位秒',
        `sort` INT(11) NOT NULL DEFAULT '0' COMMENT '排行序号',
        PRIMARY KEY (`id`) USING BTREE,
        INDEX `created_at` (`created_at`) USING BTREE
)
COMMENT='排行榜单'
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

我们可以看到在本地数据库有一个

image.png

下一步就是我如何将读到这个表里面的数据

2.node项目

2.1 创建node项目-egg-example

1用egg.js搭建初始项目

$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i

npm run dev

一个node项目已经搭建好,用egg.js框架,因为orm会比sql更具有优势,写sql容易出错,可以简单的增删改查使用orm,但是复制的汇总等,可以使用sql,sql对于复杂的数据关系处理起来更加灵活. 推荐: SQL与ORM的优缺点

2下载sequelize orm框架

npm install --save egg-sequelize mysql2 

因为使用egg.js里面的plugins是要手动引入的,所以在 config/plugin.js 中引入 egg-sequelize 插件

'use strict'

/** @type Egg.EggPlugin */
module.exports = {
  // had enabled by egg
  // static: {
  //   enable: true,
  // }
  sequelize: {
    enable: true,
    package: 'egg-sequelize',
  },
}
2.2 配置config

在config底下建立一个新的文件,叫做config.local.js

image.png config.local.js内容

'use strict'

const nowMode = 'local'
const mysqlConfigMap = {
  local: {
    username: 'xxx',
    password: 'xxx',
    host: '127.0.0.1',
    port: xxx,
  },
}

const dataBaseMap = {
  local: {
    database: 'test', // 就是你数据库里面的库名,我上面有截图那个test
  },
}

const mysqlBaseConfig = {
  dialect: 'mysql',
  ...mysqlConfigMap[nowMode],
  timezone: '+08:00',
  define: {
    freezeTableName: true, // 防止修改表名为复数
    underscored: true, // 防止驼峰式字段被默认转为下划线
  },
}

module.exports = () => {
  return {
    // 数据库配置
    sequelize: {
      datasources: [{
        delegate: 'model',
        baseDir: 'model',
        ...dataBaseMap[nowMode],
        ...mysqlBaseConfig,
      }],
    },
  }
}

如何配置可以看sequelize搜索关键词config

image.png 接下来我们就看看如何利用orm工具来读取表中的数据

2.3 在model中写入表中的数据

在app下级建立rankList文件,文件名和表名保持一致 这个 Model 就可以在 Controller 和 Service 中通过 app.model.XXX(xxx在这里为rank_list表)访问到,不懂得可以查看 sequelize搜索关键词model

'use strict'
/* 排行榜 */
module.exports = app => {
  const { INTEGER, STRING } = app.Sequelize

  const RankList = app.model.define('rank_list', {
    id: {
      type: INTEGER(11),
      primaryKey: true,
      autoIncrement: true,
      comment: '主键',
    },
    name: {
      type: STRING(50),
      allowNull: false,
      defaultValue: '',
      comment: '排行内容',
    },
    sort: {
      type: INTEGER(11),
      allowNull: false,
      defaultValue: 1,
      comment: '排行序号',
    },
    createdAt: {
      type: STRING(256),
      allowNull: false,
      defaultValue: '',
      field: 'created_at',
      comment: '创建时间',
    },
  })
  return RankList
}

image.png

2.4 读取表中的数据,写接口

1.设置路由 找到app/router.js 'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const {
    router,
    controller: {
      home,
      rankList,
    },
  } = app
  router.get('/', home.index)
  router.get('/rankList', rankList.getRankingList)
}

相比初始页面,我多解构了controller这一个层级,上面的意思就是路由是/rankList(也就是以后前端需要访问的接口),rankList.getRankingList表示的是controller文件底下的rankList文件里面的getRankingList方法

2.在contorller文件底下建rankList文件,并且调用service数据

'use strict'

const baseController = require('./baseController')

class RankListController extends baseController {
  async getRankingList() {
    const data = await this.service.rankList.getRankingList()
    this.success({ data })
  }
}

module.exports = RankListController

因为抛出接口数据的success方法在controller里面都能用到,所以可以建立一个与rankList同级的baseController

baseController文件内容

'use strict'

const { Controller } = require('egg')

class BaseController extends Controller {
  /**
   * 公用返回成功结果的函数
   * @param { Object } Object 成功结果数据
   * @param { Number } Object.status http状态码
   * @param { Number } Object.code 业务code 状态码
   * @param { String } Object.msg 结果信息
   * @param { any } Object.data 返回的数据
   */
  success({ status = 200, code = 0, msg = 'success', data } = {}) {
    this.ctx.body = {
      code,
      msg,
      data,
    }
    this.ctx.status = status
  }
}

module.exports = BaseController

一般来说前端只要判断code = 0 就表示成功,然后返回对应的数据格式

image.png

rankList文件内容

'use strict'

const baseController = require('./baseController')

class RankListController extends baseController {
  async getRankingList() {
    const data = await this.service.rankList.getRankingList()
    this.success({ data })
  }
}

module.exports = RankListController

这里RankListController已经不是继承父类Controller,而是继承baseController, Controller相当于RankListController的爷爷.

controller目前不直接从表里面获取数据,而是调用service,让service从表里面获取数据

3.service来获取表里面的数据 egg.js官网

要理解为什么要分为service和controller,可以查看官方文档

controller负责解析用户的输入,处理后返回相应的结果

service就是在复杂业务场景下用于做业务逻辑封装的一个抽象层,写sql,或者用orm处理数据,从表里面读取数据

service底下rankList内容

'use strict'

const Service = require('egg').Service

class rankListService extends Service {
  /**
   * 排行榜
   */
  async getRankingList() {
    const { rows, count } = await this.ctx.model.RankList.findAndCountAll({
      attributes: { exclude: [ 'createdAt', 'updatedAt' ] },
    })
    return { list: rows, total: count }
  }
}

module.exports = rankListService

2.5根据路由读取数据

image.png

3前端调用接口数据

3.1 用vue-cli创建一个前端vue项目

全局安装

npm install -g @vue/cli

创建项目project

vue create project

进入文件夹

cd project

运行

npm run serve

先安装axios

npm install axios --save

建立request文件以及api文件

image.png

参考: vue-cli 使用axios 模拟请求接口数据

request内容

import axios from 'axios'

var baseURL = 'http://127.0.0.1:7001'

const service = axios.create({
  baseURL: baseURL,
  timeout: 100000 // request timeout
})

service.interceptors.request.use(
  config => {

    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(

  response => {
    const res = response.data
    // console.log('resp:')
    // console.log(res)
    // if the custom code is not 0, it is judged as an error.
    if (res.code !== 0) {
      alert('!==0')
      return Promise.reject(res.message || 'error')
    } else {
      return res.data
    }
  },
  error => {
    console.log('err' + error) // for debug
    let msg = error.message
    console.log(msg);
    return Promise.reject(error)
  }
)

export default service

rankList api 内容

import request from '@/utils/request'

export function getRankList() {
  return request({
    url: '/rankList',
    method: 'get',
  })
}
3.2调用

template

<div style="border-top: 1px solid red">
   <div v-for="(item, index) in rankList" :key="index">
     序号: {{ item.sort }}
     排行名: {{item.name}}
   </div>
</div>

javascript

import { getRankList } from '@/api/rankList'
export default {
  name: 'HelloWorld',
  data() {
    return {
      rankList: [],
    }
  },
  async created() {
    const { list } = await getRankList()
    this.rankList = list
    console.log(this.rankList, 'this.rankList')
  }
}
3.3显示效果

image.png

跨域了,这里应该在服务端设置一下跨域

1.先下载egg-cors

 npm i egg-cors --save

2.在plugin里面配置,config.default.js配置

'use strict'

/** @type Egg.EggPlugin */

module.exports = {
  // had enabled by egg
  // static: {
  //   enable: true,
  // }
  sequelize: {
    enable: true,
    package: 'egg-sequelize',
  },
  cors: {
    enable: true,
    package: 'egg-cors',
  },
}

config.default.js

  config.cors = {
    origin: '*',
    allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
  };

3.运行

npm run dev

最终效果

image.png

欢迎大家一起交流,珍惜每一天,过好当下-_-