服务端编程——异常+校验器+环境变量

一、异常处理

  • 在服务端编程的时候注意要进行异常的处理,当某处出现错误的时候要及时捕捉
  • 通常异常处理分为两步:
    • 监听错误
    • 输出给客户端一段有意义的信息(错误提示)
  • 通常在服务端捕捉到的错误不会直接返回给客户端,捕捉到的错误包含了 堆栈调用信息,而要返回的是简介明了的错误提示信息
    • message:错误信息文本
    • error_code:后端自己设置的在不同情况下发出的错误码,前端会通过error_code的不同数字来判断不同错误
    • request_url:当前请求的url
  • 通常错误可以分为:
    • 已知错误:如输入url的参数校验出现错误(即可以预知的错误)
    • 未知错误:程序潜在错误,如用户输入数据库密码时输错了(很难预知的错误)

捕获错误

  • 肯定是要用try..catch语句来捕获错误的,但错误的捕获一定要先保证程序是在同步的顺序下运行,异步的时候错误捕捉不到

  • 那如何保证在一系列函数链式调用的时候让每个函数都是同步的方式运行?答案显示是利用promise、async、await来实现

  • 这里可以使用面向切面编程(AOP),也就是在整个函数调用链最上面再写一个函数来起始 捕捉整个链上出现的错误,并且在另一个文件里写上对该错误的处理逻辑

  • 这里是在中间件函数里出现错误时抛出异常,然后在 middlewares/exceptions.js 文件里统一写处理错误逻辑,注意这个文件里的函数要:

    • 在最先被注册,并且调用next
    • 一定要是async函数,且调用next时要写await,都要保证程序是同步运行的
const catchError = async (ctx, next) => {
  try {
    // 确定下一个中间件函数要继续执行下去
    await next()
  } catch (error) {
    if (error.error_code) {
    // 判断一下是否是已知错误
      error.requestUrl = `${ctx.method} ${ctx.path}`
      ctx.body = {
        msg: error.msg,
        code: error.code,
        requestUrl: error.requestUrl
      }
      ctx.code = error.code
    }
  }
}

定义HttpException异常基类

  • 由于是要throw出一个错误,里面要包含所有错误信息,因此是要new一个Error类然后在里面添加信息的,现在将这个过程封装成一个HttpException异常类,里面专门存放有关的异常信息
  • 这个类继承js的原生Error类
class HttpException extends Error {
  constructor (msg="啊哦,出现了一个错误", error_code=10000, code=200) {
    // Class中的 super(),它在这里表示父类的构造函数,用来新建父类的 this 对象
    super()
    this.msg = msg
    this.error_code = error_code
    this.code = code
  }
}
module.exports = HttpException
  • 后续在中间件函数里要抛出异常的时候,就引入这个文件,并且抛出HttpException类型的错误就好了,并传递参数

  • 可能有人会发现,欸这HttpException怎么还是基类?因为后续各种特定的错误还要定制属于自己的特殊类(很多哦),这样在创建抛出错误的时候都不太需要传参了,例如参数错误类

class ParameterException extends HttpException {
  constructor (msg, error_code, code) {
    super()
    this.msg = msg || '参数错误'
    this.error_code = error_code || 10000
    this.code = 400 || code
  }
}

二、校验器(Lin-Validator)

  • 由于koa真的非常精简,所以很多功能都需要自己封装文件去实现,现在要用的校验功能也一样,这里使用给定的校验器Lin-Validator(七月老师自己写的)
    • 起始这里说到的校验功能,就是类似于 传参的时候要传正整数 等情况,不做约束的话拿到所有参数没法处理,传参错误则报错
  • 将lin-validator.js和util.js放到core文件夹下,在app下添加validator文件夹,里面专门放和校验有关的代码

创建校验器

  • 在validator文件夹下写validator.js文件,先拿限制正整数的校验器为例:
    • 创建PositiveIntegerValidator类,继承LinValidator类(要引入LinValidator和Rule两个类)
    • 构造函数里写上要校验的规则和校验失败时的错误信息
class PositiveIntegerValidator extends LinValidator {
  constructor () {
    super()
    this.id = [
      // 规则可以叠加,是 且 的关系
      new Rule('isInt', '需要正整数', {min: 1})
    ]
  }
}

使用校验器

  • 在要使用的文件里,像这样创建一个对象 调用validate函数将上下文信息传递进去
const v =  await new PositiveIntegerValidator().validate(ctx)
  • 这里的await和async的错误找了好久... 控制台总是报错说Promise的reject未进行处理,但明明已经写了try...catch逻辑,原来还是因为try...catch只能捕获同步的错误,这里用await和async来让代码同步执行
  • 注意在校验的时候,如果要拿到请求body里的数据,一定要在挂载中间件之前就引入koa-bodyparse,不然拿不到body(body为undefined)
  • 校验器这里返回的v对象,不加await的话返回的是Promise对象,加上了就能将promise的值求出来,这里通过v可以直接获取当前请求中的信息,比如:
    • 要获取body中传过来的email,v.get('body.email')
    • 要获取header中传过来的token,v.get('header.token')

三、环境配置

  • 由于在服务器编程时,错误信息一旦被catch了就看不到了,也就没办法进行处理,所以调试的时候还是要将error给throw出来
  • 但真正项目上线之后,错误是不能直接展现给用户的,因此要区分生产环境和开发环境,根据不同的环境来选择某些输出操作进不进行
  • 这里在根目录下创建config文件夹里面放着环境变量的相关文件,dev是开发环境,prod是生产环境
  • 在init里将环境变量导入到全局(global),要注意路径的问题(这里又再一次用到了process.cwd()函数来获取当前的绝对路径)
  • 从此在每一个输出调试信息的地方都先判断当前是在什么环境下运行
原文地址:https://www.cnblogs.com/TRY0929/p/13854271.html