搭建egg框架
(记录封装ctx.body) (异常处理、参数校验监听拦截中间件) (参数校验、参数过滤中间件) (token校验中间件) (路由配置) (参数校验配置)
gitee地址在文章后面
中间件
1.封装ctx.body
// app/middleware/init.js
//初始化中间件
module.exports = options => {
return async (ctx, next) => {
ctx.returnBody = (code = 200, msg = '', data = {}) => {
ctx.status = code;
ctx.body = { code, msg, data, time: Math.floor(Date.now())};
return;
}
await next();
}
};
//使用
ctx.returnBody(code, msg, data);
2.异常处理、参数校验监听拦截中间件
// app/middleware/errorHanlder.js
//异常处理、参数校验监听拦截中间件
module.exports = () => {
return async (ctx, next)=> {
try {
await next();
} catch (err) {
// 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
ctx.app.emit('error', err, ctx)
const status = err.status || 500
const message = status === 422 ? '参数校验未通过' : 'Internal Server Error';
// HTTP Code
ctx.status = status;
// 生产环境
const isProd = ctx.app.config.env === 'prod';
// 错误响应对象 (status === 422 参数校验监听拦截)
const msg = (status === 500 && isProd) ? 'Internal Server Error' : message;
const data = status === 422 ? err.errors.map((item) =>{
• return `${item.field}-------------------${item.message}`;
}) : undefined;
ctx.returnBody(status, msg, data);
}
}
}
//参数校验在下一条中间件 app/middleware/requier.js,参数校验不通过会抛出422的错误
3.参数校验、参数过滤中间件
//参数校验、参数过滤中间件
const validateData = require('../validateConfig')
module.exports = options => {
return async (ctx, next) => {
let url = ctx.request.url;
// 拆分请求url,获取到controller和方法名
url = url.split('?')[0].split('/')
let valiData = validateData;
url.forEach(item => {
if (item) {
• valiData = valiData[item];
}
});
checkPparams(ctx);
//参数校验
if (valiData) {
ctx.validate(valiData, ctx.params)
}
await next();
}
};
//过滤请求参数
const checkPparams = (ctx) => {
//GET非严格模式ctx.query / 严格模式ctx.params POST 》》 ctx.request.body
let paramsData = {};
if (ctx.request.method === 'GET') {
paramsData = ctx.query || ctx.params || {};
} else {
paramsData = ctx.request.body || {};
}
// 请求参数
ctx.params = paramsData;
}
// 控制层controller中可直接用ctx.params获取到请求参数
// 得安装egg-validate npm install --save egg-validate
// app/validateConfig存放接口参数校验规则
4.token校验中间件
//token校验中间件
module.exports = options => {
return async (ctx, next) => {
let tgt = ctx.request.header.tgt;
if (!tgt) return ctx.returnBody(401, 'token不存在');
const token = ctx.session.tokenList || [];
const tgtFlag = tgt && token.includes(tgt);
if (!tgtFlag) return ctx.returnBody(401, 'token错误或已失效');
await next();
}
};
// token的设置,在登陆时设置相应session
中间件/session/egg安全策略/jwt配置
/* eslint valid-jsdoc: "off" */
'use strict';
/**
* @param {Egg.EggAppInfo} appInfo app info
*/
module.exports = appInfo => {
/**
* built-in config
* @type {Egg.EggAppConfig}
**/
const config = exports = {};
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1637825072263_7631';
// 中间件加载
config.middleware = ['init', 'errorHanlder', 'token', 'requier'];
config.token = {
ignore: ['/reg', '/User/login'] // tonke校验忽略注册和登陆的接口
};
// session 配置
config.session = {
key: 'EGG_SESS', //eggjs默认session的key
maxAge: 24 * 3600 * 1000, // 1 day
httpOnly: true,
encrypt: true,
renew: true //每次访问页面都会给session会话延长时间
};
// 针对egg的安全策略,过滤以下接口不用提交x-csrf-token
config.security = {
csrf: {
ignore: '/User/login',
enable: false
},
};
//jwt的配置
config.jwt = {
key: 'dwanliang120',
};
return {
...config,
...userConfig,
};
};
//注意中间件顺序,中间件有相互调用可能
路由配置
// app/router.js
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
require('./router/app/Home')(app);
require('./router/app/User')(app);
};
// app/router/app/user.js
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
router.get('/User/userList', controller.user.userList);
router.post('/User/login', controller.user.login);
};
参数校验配置
// app/validateConfig/index.js
'use strict';
const userValidate = require('./user')
//参数校验数据
module.exports = {
// 对应路由的/User/userList的(User)
User: {
...userValidate
}
}
'use strict';
// app/controller/user.js的参数校验
module.exports = {
// 对应路由的/User/userList的(userList)
'userList': {
username: { type: 'string', required: false},
packageName: { type: 'string', required: false},
// sort: { type: 'number',required: true },
},
login: {
username: { type: 'string'},
}
}
jwt生成token
// app/controller/user.js
'use strict';
const Controller = require('egg').Controller;
class UserController extends Controller {
async userList() {
const { ctx, service } = this;
ctx.body = ctx.params;
}
async login() {
const { ctx } = this;
const params = ctx.params;
const token = await this.setToken({username: params.username});
return ctx.returnBody(200, '登陆成功!', token);
}
async setToken(data) {
const { ctx, app } = this;
const token = app.jwt.sign(data, app.config.jwt.key);
if (!token) return;
if (ctx.session.tokenList) {
ctx.session.tokenList.push(token);
} else {
ctx.session.tokenList = [token];
}
return token;
}
}
module.exports = UserController;
//需安装JWT npm install egg-jwt --save
// http://localhost:7001/User/login调用接口,生成token
giter地址: gitee.com/dwanliang/e…
代码中还有mysql的使用,如不需要可修改 config/config.default.js的配置,egg-mysql的介绍使用可参考上一篇文章 记录egg连接mysql配置