前言
使用express和koa2开发api都比较自由没有一个统一的开发规范,并且让初学者学起来感觉有点混乱,使用egg.js开发api有一种用vue开发的感觉非常舒服,但是使用egg.js其实里面还有挺多道道的,搭个开发环境顺便学习一下egg.js不是美滋滋。
1.搭建egg.js脚手架
mkdir egg-example && cd egg-example
cnpm init egg --type=simple
cnpm i
npm run dev
open http://localhost:7001
2.跨域配置
cnpm i egg-cors --save
// app/config/plugin.js
exports.static = true;
// 跨域
exports.cors = {
enable: true,
package: 'egg-cors',
};
// app/config/config.default.js
// 安全配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['*']
};
// 跨域配置
config.cors = {
origin: '*',
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
};
3.安装egg-swagger-doc
cnpm i egg-swagger-doc --save
egg-swagger-ui文档
// app/config/plugin.js
// swagger文档
exports.swaggerdoc = {
enable: true,
package: 'egg-swagger-doc',
};
// app/config/config.default.js
// swagger文档配置
config.swaggerdoc = {
dirScanner: './app/controller', //插件扫描的文档路径
apiInfo: {
title: 'swagger文档',
description: 'egg.js swagger-demo文档',
version: '1.0.0',
},
consumes: ['application/json','multipart/form-data'], // 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html
produces: ['application/json','multipart/form-data'], // 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
schemes: ['http', 'https'],
routerMap: true, // 是否自动生成route
enable: true,
};
// app/router.js
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
//重定向到swagger-ui.html
app.router.redirect('/', '/swagger-ui.html', 302);
}
1.在app目录下新建文件夹 contract(必须) 和 service(备用)
1.1在contract目录下新建目录response 和 request 目录
1.2在response目录下新建base.js
// app/contract/response/base.js
'use strict';
module.exports = {
// 测试模块
testResponse: {
message: { type: 'string' }
}
};
设置一下测试
2.在controller目录下把home.js改成test.js
'use strict';
const Controller = require('egg').Controller;
/**
* @Controller 测试
*/
class TestController extends Controller {
/**
* @summary 接口测试
* @description 测试swagger文档是否可用
* @router get /api/v1/test
* @request query string str 随机字符串
* @response 200 testResponse
*/
async test() {
const { ctx } = this;
const str = ctx.query.str
ctx.body = await {
message: 'swagger is OK!!! and query is:' + str
}
}
}
module.exports = TestController;
休息一下....npm run dev 跑一下看看能跑动不 如能出现swagger界面就继续
4.设置调试
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Egg",
"type": "node",
"request": "launch",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "npm",
"windows": { "runtimeExecutable": "npm.cmd" },
"runtimeArgs": [ "run", "debug" ],
"console": "integratedTerminal",
"protocol": "auto",
"restart": true,
"port": 9229,
"autoAttachChildProcesses": true
}
]
}
6.安装mysql
7.初始化数据库
初始化三个数据 开发环境数据库 测试环境数据库 生产环境数据库
mysql -u root -p -e 'CREATE DATABASE IF NOT EXISTSdev_db
;'
mysql -u root -p -e 'CREATE DATABASE IF NOT EXISTStest_db
;'
mysql -u root -p -e 'CREATE DATABASE IF NOT EXISTSpord_db
;'
没有密码的话可以把 -p 去掉
8.安装egg-sequelize sequelize-cli mysql2
cnpm install --save egg-sequelize mysql2
cnpm install --save-dev sequelize-cli
egg-sequelize文档
mysql2文档
sequelize-cli文档
egg.js sequelize教程
使用 sequelize-cli 可以创建模型和迁移文件 同步到数据库中 (但是我不这样做)
// app/config/plugin.js
// sequelize ORM
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
// app/config/config.default.js
// 访问数据库
config.sequelize = {
dialect: 'mysql', // 数据库类型,支持 mysql,sqlite,mssql,pgsql,oracle。
host: '127.0.0.1', // 数据库服务器地址。
port: 3306, // 数据库连接端口号。
database: 'dev_db', // 数据库名称。
username: "root", // 数据库登录用户名。
password: "111111", // 数据库登录密码。
timezone: '+08:00', // 时区 东八区
underscored: true,// 是否自动进行下划线转换(这里是因为DB默认的命名规则是下划线方式,而我们使用的大多数是驼峰方式)
define: {
freezeTableName: true, // Model 对应的表名将与model名相同。
timestamps: false // 默认情况下,Sequelize会将createdAt和updatedAt的属性添加到模型中,以便您可以知道数据库条目何时进入数据库以及何时被更新。
}
};
8.1 在 egg 项目中,我们希望将所有数据库 Migrations 相关的内容都放在 database 目录下,所以我们在项目根目录下新建一个 .sequelizerc 配置文件:
'use strict';
const path = require('path');
module.exports = {
config: path.join(__dirname, 'database/config.json'),
'migrations-path': path.join(__dirname, 'database/migrations'),
'seeders-path': path.join(__dirname, 'database/seeders'),
'models-path': path.join(__dirname, 'app/model'),
};
8.2 初始化 Migrations 配置文件和目录
npx sequelize init:config
npx sequelize init:migrations
8.3 如果出现了database目录 那这一步就操作成功了。
执行完后会生成 database/config.json 文件和 database/migrations 目录,我们修改一下 database/config.json 中的内容,将其改成我们项目中使用的数据库配置:
根据egg.js对数据库的环境配置
{
"development": {
"username": "root",
"password": 111111,
"database": "dev_db",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
},
"test": {
"username": "root",
"password": 111111,
"database": "test_db",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
},
"production": {
"username": "root",
"password": 111111,
"database": "prod_db",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
}
}
到了这一步就可以建模型(表)了
9.安装 egg-sequelize-auto
但是我在网上找到了一个叫做 egg-sequelize-auto 的东东,可以自动对照数据库表生成模型
个人感觉在sequelize生成模型操作比较复杂,一般都是在外面把表设计好之后再开始写模型的
通过表自动生成模型不是美滋滋~~~
cnpm install -g egg-sequelize-auto
egg-sequelize-auto文档
刚我们安装过mysql2了 但是是安装在--save
这里是全局安装 还需要全局安装一个mysql2
cnpm install mysql2 -g
-o 表示生成 models 的路径,-h 表示主机,-p 表示端口,-d 表示数据库, -u 表示用户名,-x 表示密码 -e 表示数据库
// package.json
// 在scripts里添加
"scripts": {
"dev_db": "egg-sequelize-auto -o ./app/model -h 127.0.0.1 -p 3306 -d dev_db -u root -x 111111 -e mysql",
"test_db": "egg-sequelize-auto -o ./app/model -h 127.0.0.1 -p 3306 -d dev_db -u root -x 111111 -e mysql",
"pord_db": "egg-sequelize-auto -o ./app/model -h 127.0.0.1 -p 3306 -d dev_db -u root -x 111111 -e mysql",
},
在package.json的scripts里创建3条命令,分别对应刚刚初始化的3个环境的数据库。
生产环境 : dev_db
测试环境 : test_db
生产环境 : pord_db
接着我们使用可视化数据库创个user表(万金油表)这样比较直观
然后我们跑一下
npm run dev_db
如果在app目录下出现model文件夹下有个user.js那就是模型创建成功啦!
10.写一个新增用户的控制器来对swagger和sequelize熟悉一下
10.1 --- 在controller目录下新建user.js 写一个新增user的方法
// vscode可以设置api快速代码片段 @@直接提示
{
"swaggerApi": {
"scope": "javascript,typescript",
"prefix": "@@",
"body": [
"/**",
"* @summary 接口详情",
"* @router get|post /api/v1/$1",
"* @Request body|query 请求体",
"* @response 200 baseResponse",
"*/",
"async 方法名() {",
" const { ctx, service } = this;",
" ctx.validate(ctx.rule.请求体, ctx.request.body|ctx.query);",
" ctx.body = await service.模块.方法名()",
"}"
],
"description": "swaggerApi快速创建"
}
}
// app/controller/user.js
'use strict';
const Controller = require('egg').Controller;
/**
* @Controller 用户模块
*/
class UserController extends Controller {
/**
* @summary 创建用户
* @router post /api/v1/create-user
* @Request body createUserReq
* @response 200 baseResponse
*/
async create() {
const { ctx, service } = this;
// 参数验证
ctx.validate(ctx.rule.createUserReq, ctx.request.body);
ctx.body = await service.user.createUser()
}
}
module.exports = UserController;
然后在app/contract/request目录下新建一个user.js
用作swagger获取参数后的对参数的验证
根据app/model/user.js的模型创建验证参数模型
// app/contract/request/user.js
'use strict';
module.exports = {
// 创建用户参数
createUserReq: {
phone: {
type: 'string',
example: '13723456789',
format: /^1[34578]\d{9}$/,
description: '电话',
required: true
},
password: {
type: 'string',
example: '密码',
maxLength:'36',
required: true
},
name: {
type: 'string',
example: '姓名',
maxLength:'10',
required: true
},
sex: {
type: 'string',
enum: ['M', 'F'],
description: '性别 M:男 F:女'
},
balance: {
type: 'number',
example: 666.66,
description: '金额'
}
}
};
安装egg-validate
cnpm i egg-validate --save
鸡蛋验证文档
// app/config/plugin.js
// 验证方案
exports.validate = {
enable: true,
package: 'egg-validate',
};
安装 moment.js(处理时间的工具)+ uuid (自动生成id)
cnpm i moment uuid --save
momentjs中文网
在app目录下新建扩展文件夹extend 取名为helper.js
通过 ctx.helper 访问到 helper 对象
// app/extend/helper.js
const moment = require('moment');
moment.locale('zh-cn')
exports.Date = v => moment()
exports.zhDate = v => moment().format('lll')
编写user服务
// app/service/user.js
'use strict';
const Service = require('egg').Service;
const UUID = require('uuid');
class UserService extends Service {
/**
* @创建用户
*/
async createUser() {
const { ctx } = this
try {
const id = UUID.v1()
const class_id = null
return await ctx.model.User.create({ id, class_id, ...ctx.request.body });
} catch (error) {
// 记录错误日志
this.logger.error(`${ctx.helper.zhDate()} : user服务创建时发生错误 --- ${error}`)
const errorBody = {
code: 5000,
data: null,
message: '参数有毒:' + error
}
return errorBody
}
}
}
module.exports = UserService;
此时创建用户的服务就写好了
可以使用swagger里的try it out测试接口
如果出现错误日志也会自动打印
在logs文件夹下可以找到错误的日志