Egg中参数校验和异常处理的实践

参数校验

手动校验

之前的参数都是在Controller的入口处,手动的进行校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async index() {
  const {ctx } = this
  const { query } = ctx.request
  try {
    const { type } = query
    // 缺少参数,没法查
    if (!type) {
      const errMsg = '缺少参数'
      ctx.response.status = this.config.httpCodeHash.badRequest
      ctx.response.body = ctx.helper.makeErrorResponse(errMsg)
      this.logger.error(new Error(errMsg))
      return
    }
    // 响应内容
    const data = await ctx.service.settings.findSettings(type)
    ctx.response.status = this.config.httpCodeHash.ok
    ctx.response.body = data
  } catch (err) {
    ctx.response.body = err.message || '查询规则错误'
    ctx.response.status = this.config.httpCodeHash.serverError
    this.logger.error(err)
  }
}

这样很很导致大量的代码冗余,每个Controll都要写这样进行校验,如果失败手动返回错误结果(实际上参数校验失败也应该统一处理,后面的异常处理部分会提到)

egg-validate

实际上使用egg-validate插件可以大大简化和标准化参数校验的流程。

安装:

1
npm i egg-validate --save

需要在plugin.js中开启插件:

1
2
3
4
5
// config/plugin.js
exports.validate = {
  enable: true,
  package: 'egg-validate',
};

egg-validate实际上是由parameter这个库封装而来,它可以针对很多类型的参数进行校验,比如stringdateTimenumberenum等,具体的使用方法可以参考它的文档。

使用egg-validate进行参数校验的正确姿势:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
'use strict'
const Controller = require('egg').Controller

// 创建规则的校验规则
const createRule = {
  type: { type: 'enum', values: [ 'pre', 'single', 'other' ] },
  name: { type: 'string', trim: true },
  packageName: { type: 'string', trim: true },
  content: { type: 'object' },
}

class PrivacyController extends Controller {
  // 新建预设规则
  async create() {
    const { ctx } = this
    const { name, packageName, type, content } = ctx.request.body

    // 参数校验
    ctx.validate(createRule, ctx.request.body)

    // 创建新规则
    const data = await ctx.service.settings.createSetting(name.trim(), packageName.trim(), type.trim(), content)
    
    // 创建成功
    ctx.response.status = this.config.httpCodeHash.created.code
    ctx.response.body = insertSetting
  }
}

module.exports = PrivacyController

ctx.validate的第一个参数就是校验的规则,第二个参数是被校验的参数,我们的请求方法是POST,所有的参数都在body中,所以传入的是ctx.request.body

如果参数校验没有通过,将会抛出一个status422的异常:

这个错误我们没有在Controller中捕获,后面会提到是如何处理的。

要注意的是,在校验规则中,某些类型是可以传入自定义的错误提示信息的,比如对string的校验,如果使用了formate选项,那么传入的message就会有效,其他时刻传入message无效,无法自定义错误提示信息:

1
2
3
4
const indexRule = {
  id: { type: 'string', trim: true, format: /^.{24}$/, message: '非法ID' }, // Mongo生成的ID长度为24位
  packageName: { type: 'string', trim: true },
}

查看它的源码,发现它只有显示或者隐式(typeemail等)这种情况下才会提示自定义的提示信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function checkString(rule, value) {
  if (typeof value !== 'string') {
    return this.t('should be a string');
  }

  // if required === false, set allowEmpty to true by default
  if (!rule.hasOwnProperty('allowEmpty') && rule.required === false) {
    rule.allowEmpty = true;
  }

  var allowEmpty = rule.hasOwnProperty('allowEmpty')
    ? rule.allowEmpty
    : rule.empty;

  if (!value) {
    if (allowEmpty) return;
    return this.t('should not be empty');
  }

  if (rule.hasOwnProperty('max') && value.length > rule.max) {
    return this.t('length should smaller than %s', rule.max);
  }
  if (rule.hasOwnProperty('min') && value.length < rule.min) {
    return this.t('length should bigger than %s', rule.min);
  }

  if (rule.format && !rule.format.test(value)) {
    return rule.message || this.t('should match %s', rule.format);
  }
}

function checkEnum(rule, value) {
  if (!Array.isArray(rule.values)) {
    throw new TypeError('check enum need array type values');
  }
  if (rule.values.indexOf(value) === -1) {
    return this.t('should be one of %s', rule.values.join(', '));
  }
}

有时间想提一个PR,支持所有的类型校验都支持自定义提示信息,但是现在由于无法完全自定义,所以索性在异常处理的时候不对外暴漏具体的message了,只给出统一的参数校验失败的提示:

1
2
3
4
{
  "code": -1,
  "message": "Validation Failed"
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值