mkdir egg-example && cd egg-example
npm init egg --type=simple
npm i
cd app/
mkdir service middleware schedule extend
cd ../config/
touch config.prod.js config.unittest.js config.local.js
cd ../test/app/
mkdir service extend
修改端口号要在指定的配置文件中修改,修改的文件名称是config.[env].js。例如本地开发时将端口号改为9100,则打开config.local.js文件,添加下面的代码:
// edit port
config.cluster = {
listen: {
path: '',
port: 9100,
hostname: '0.0.0.0',
},
};
运行项目,在浏览器中访问http://localhost:9100/,有内容输出表示修改成功。
安装mysql插件:
npm i --save egg-mysql
开启插件:
// config/plugin.js
exports.mysql = {
enable: true,
package: 'egg-mysql',
};
配置单个数据库:
// config/config.${env}.js
config.mysql = {
// 单数据库信息配置
client: {
// host
host: 'mysql.com',
// 端口号
port: '3306',
// 用户名
user: 'test_user',
// 密码
password: 'test_password',
// 数据库名
database: 'test',
},
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
};
测试数据连接是否成功:
// app/service/user.js
const Service = require('egg').Service;
class UserService extends Service {
async getUserInfo(uid) {
const { app } = this;
try {
// 相当于 select * from user where id=1 limit 0,1;
const user = await app.mysql.get('user', { id: uid });
return user;
} catch (e) {
return null;
}
}
}
module.exports = UserService;
// app/controller/user.js
const Controller = require('egg').Controller;
class UserController extends Controller {
async getUserInfo() {
const { ctx, service } = this;
// 获取路由上面预定义的参数
const uid = ctx.params.uid || '';
const result = await service.user.getUserInfo(uid);
if (result) {
ctx.body = JSON.stringify(result);
} else {
ctx.body = 'can not find user!';
}
}
}
module.exports = UserController;
// app/router.js
module.exports = app => {
const { router, controller } = app;
router.get('/user/find/:uid', controller.user.getUserInfo);
};
打开浏览器输入http://localhost:9100/user/find/1,页面输出数据库的数据则表示正常。
使用自定义中间件实现接口返回格式的同一整理。
创建辅助方法:
// app/extend/context.js
function formatResponse(code = 200, data = null, err_msg = '', tips = '') {
return {
code,
data,
err_msg,
tips,
};
}
module.exports = {
formatResponse,
};
创建中间件:
//app/middleware/formatResponse.js
module.exports = () => {
return async function formatResponse(ctx, next) {
await next();
if (ctx.status === 200) {
ctx.body = ctx.formatResponse(ctx.status, ctx.body);
} else {
ctx.body = ctx.formatResponse(ctx.status, null, ctx.body);
}
};
};
在配置文件中添加中间件:
//config/config.default.js
......
config.middleware = [ 'formatResponse' ];
......
安装插件:
npm i bcrypt
使用插件:
// app/extend/encryption.js
const bcrypt = require('bcrypt');
const saltRounds = 10;
/**
* 获取加密后的密码
* @param {String} pwdText 密码字符串
*/
exports.getBcryptPWD = (pwdText = '') => bcrypt.hashSync(pwdText, saltRounds);
/**
* 比较密码和数据库中的加密密码
* @param {String} pwdText 密码字符串
* @param {String} hash 加密密码
*/
exports.comparePWD = (pwdText = '', hash = '') => {
return bcrypt.compareSync(pwdText, hash);
};
// app/extend/helper.js
const { getBcryptPWD, comparePWD } = require('./encryption.js');
module.exports = {
getBcryptPWD,
comparePWD,
};
使用插件:
// app/controller/user.js
const Controller = require('egg').Controller;
class UserController extends Controller {
async addUser() {
const { app, ctx, service } = this;
const { name, password } = ctx.request.body;
try {
const result = await service.user.addUser(name, ctx.helper.getBcryptPWD(password));
ctx.body = `Bearer ${app.jwt.sign({
id: result.insertId,
name,
password,
}, app.config.jwt.secret)}`;
} catch (err) {
ctx.status = 500;
ctx.body = '用户创建失败';
}
}
async verifyUser() {
const { ctx, service } = this;
const { name: postName = '', password: postPWD = '' } = ctx.request.body;
try {
const user = await service.user.findUser(postName);
if (ctx.helper.comparePWD(postPWD, user.password)) {
ctx.body = 'ok';
} else {
ctx.status = 500;
ctx.body = '用户不存在';
}
} catch (err) {
ctx.status = 500;
ctx.body = '用户名重复';
}
}
}
module.exports = UserController;
注意:
使用token对用户进行验证;
安装插件:
npm install egg-jwt --save
配置插件:
// config/plugin.js
// use jwt
exports.jwt = {
enable: true,
package: 'egg-jwt',
};
配置文件设置参数:
// config/config.default.js
// config jwt
config.jwt = {
secret: appInfo.name, // 自定义加密使用的字符串
expiresIn: '7d', // 定义jwt的有效时间
};
配置需要token验证的路由:
// app/router.js
module.exports = app => {
const { router, controller, jwt } = app;
router.post('/user/add', controller.user.addUser);
router.post('/user/verify', jwt, controller.user.verifyUser);
};
使用jwt:
// app/controller/user.js
......
async addUser() {
const { app, ctx, service } = this;
const { name, password } = ctx.request.body;
try {
const result = await service.user.addUser(name, ctx.helper.getBcryptPWD(password));
ctx.body = `Bearer ${app.jwt.sign({
id: result.insertId,
name,
password,
}, app.config.jwt.secret)}`;
} catch (err) {
ctx.status = 500;
ctx.body = '用户创建失败';
}
}
......
// test/app/service/user.test.js
'use strict';
const { app, assert } = require('egg-mock/bootstrap');
const randomNum = Math.floor(Math.random() * 10000);
describe('test/app/service/user.test.js', () => {
// 向数据库查询数据
it('should insert user', async () => {
const ctx = app.mockContext();
try {
const result = await ctx.service.user.addUser(`王${randomNum}`, ctx.helper.getBcryptPWD(`${randomNum}`));
assert.ok(result);
console.log(`insert result: ${JSON.stringify(result)}`);
} catch (err) {
assert.ok(false);
}
});
// 测试数据库查询
it('should find user', async () => {
const ctx = app.mockContext();
const user = await ctx.service.user.findUser(`王${randomNum}`);
console.log(`find user: ${JSON.stringify(user)}`);
assert.ok(user);
});
});
运行测试:
npm run test-local
在配置文件中关闭csrf防护
// config/config.default.js
// close csrf protect
config.security = {
csrf: {
enable: false,
},
};