前言
使用express和koa2開發(fā)api都比較自由沒有一個統(tǒng)一的開發(fā)規(guī)范,并且讓初學(xué)者學(xué)起來感覺有點(diǎn)混亂,使用egg.js開發(fā)api有一種用vue開發(fā)的感覺非常舒服,但是使用egg.js其實(shí)里面還有挺多道道的,搭個開發(fā)環(huán)境順便學(xué)習(xí)一下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'], // 指定處理請求的提交內(nèi)容類型(Content-Type),例如application/json, text/html
produces: ['application/json','multipart/form-data'], // 指定返回的內(nèi)容類型,僅當(dāng)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' }
}
};
設(shè)置一下測試
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 隨機(jī)字符串
* @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 跑一下看看能跑動不 如能出現(xiàn)swagger界面就繼續(xù)

4.設(shè)置調(diào)試
// .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.初始化數(shù)據(jù)庫
初始化三個數(shù)據(jù) 開發(fā)環(huán)境數(shù)據(jù)庫 測試環(huán)境數(shù)據(jù)庫 生產(chǎn)環(huán)境數(shù)據(jù)庫
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 去掉
在vscode里面搞
可以看到數(shù)據(jù)庫初始化了
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 可以創(chuàng)建模型和遷移文件 同步到數(shù)據(jù)庫中 (但是我不這樣做)
// app/config/plugin.js
// sequelize ORM
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
// app/config/config.default.js
// 訪問數(shù)據(jù)庫
config.sequelize = {
dialect: 'mysql', // 數(shù)據(jù)庫類型,支持 mysql,sqlite,mssql,pgsql,oracle。
host: '127.0.0.1', // 數(shù)據(jù)庫服務(wù)器地址。
port: 3306, // 數(shù)據(jù)庫連接端口號。
database: 'dev_db', // 數(shù)據(jù)庫名稱。
username: "root", // 數(shù)據(jù)庫登錄用戶名。
password: "111111", // 數(shù)據(jù)庫登錄密碼。
timezone: '+08:00', // 時區(qū) 東八區(qū)
underscored: true,// 是否自動進(jìn)行下劃線轉(zhuǎn)換(這里是因?yàn)镈B默認(rèn)的命名規(guī)則是下劃線方式,而我們使用的大多數(shù)是駝峰方式)
define: {
freezeTableName: true, // Model 對應(yīng)的表名將與model名相同。
timestamps: false // 默認(rèn)情況下,Sequelize會將createdAt和updatedAt的屬性添加到模型中,以便您可以知道數(shù)據(jù)庫條目何時進(jìn)入數(shù)據(jù)庫以及何時被更新。
}
};
8.1 在 egg 項(xiàng)目中,我們希望將所有數(shù)據(jù)庫 Migrations 相關(guān)的內(nèi)容都放在 database 目錄下,所以我們在項(xiàng)目根目錄下新建一個 .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 如果出現(xiàn)了database目錄 那這一步就操作成功了。
執(zhí)行完后會生成 database/config.json 文件和 database/migrations 目錄,我們修改一下 database/config.json 中的內(nèi)容,將其改成我們項(xiàng)目中使用的數(shù)據(jù)庫配置:
根據(jù)egg.js對數(shù)據(jù)庫的環(huán)境配置
{
"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
但是我在網(wǎng)上找到了一個叫做 egg-sequelize-auto 的東東,可以自動對照數(shù)據(jù)庫表生成模型
個人感覺在sequelize生成模型操作比較復(fù)雜,一般都是在外面把表設(shè)計(jì)好之后再開始寫模型的
通過表自動生成模型不是美滋滋~~~
cnpm install -g egg-sequelize-auto
egg-sequelize-auto文檔
剛我們安裝過mysql2了 但是是安裝在--save
這里是全局安裝 還需要全局安裝一個mysql2
cnpm install mysql2 -g
-o 表示生成 models 的路徑,-h 表示主機(jī),-p 表示端口,-d 表示數(shù)據(jù)庫, -u 表示用戶名,-x 表示密碼 -e 表示數(shù)據(jù)庫
// 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里創(chuàng)建3條命令,分別對應(yīng)剛剛初始化的3個環(huán)境的數(shù)據(jù)庫。
生產(chǎn)環(huán)境 : dev_db
測試環(huán)境 : test_db
生產(chǎn)環(huán)境 : pord_db
接著我們使用可視化數(shù)據(jù)庫創(chuàng)個user表(萬金油表)這樣比較直觀
user表
然后我們跑一下
npm run dev_db
如果在app目錄下出現(xiàn)model文件夾下有個user.js那就是模型創(chuàng)建成功啦!
10.寫一個新增用戶的控制器來對swagger和sequelize熟悉一下
10.1 --- 在controller目錄下新建user.js 寫一個新增user的方法
// vscode可以設(shè)置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快速創(chuàng)建"
}
}
// app/controller/user.js
'use strict';
const Controller = require('egg').Controller;
/**
* @Controller 用戶模塊
*/
class UserController extends Controller {
/**
* @summary 創(chuàng)建用戶
* @router post /api/v1/create-user
* @Request body createUserReq
* @response 200 baseResponse
*/
async create() {
const { ctx, service } = this;
// 參數(shù)驗(yàn)證
ctx.validate(ctx.rule.createUserReq, ctx.request.body);
ctx.body = await service.user.createUser()
}
}
module.exports = UserController;
然后在app/contract/request目錄下新建一個user.js
用作swagger獲取參數(shù)后的對參數(shù)的驗(yàn)證
根據(jù)app/model/user.js的模型創(chuàng)建驗(yàn)證參數(shù)模型
// app/contract/request/user.js
'use strict';
module.exports = {
// 創(chuàng)建用戶參數(shù)
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
雞蛋驗(yàn)證文檔
// app/config/plugin.js
// 驗(yàn)證方案
exports.validate = {
enable: true,
package: 'egg-validate',
};
安裝 moment.js(處理時間的工具)+ uuid (自動生成id)
cnpm i moment uuid --save
momentjs中文網(wǎng)
在app目錄下新建擴(kuò)展文件夾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服務(wù)
// app/service/user.js
'use strict';
const Service = require('egg').Service;
const UUID = require('uuid');
class UserService extends Service {
/**
* @創(chuàng)建用戶
*/
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服務(wù)創(chuàng)建時發(fā)生錯誤 --- ${error}`)
const errorBody = {
code: 5000,
data: null,
message: '參數(shù)有毒:' + error
}
return errorBody
}
}
}
module.exports = UserService;
此時創(chuàng)建用戶的服務(wù)就寫好了
可以使用swagger里的try it out測試接口
測試
如果出現(xiàn)錯誤日志也會自動打印
在logs文件夾下可以找到錯誤的日志



