记录初始化egg框架(异常处理、参数校验、参数过滤、token校验、路由配置、中间件)

1,884 阅读3分钟

搭建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配置