一个良好的编码习惯必然离不开异常处理, 本文将主要介绍如何在 koa 框架下面优雅地抛出错误, 并统一处理返回.
前言
一个良好的编码习惯必然离不开异常处理, 本文将主要介绍如何在 koa 框架下面优雅地抛出错误, 并统一处理返回.
正文
koa 是一个优秀的 NodeJS web 框架, 当我们在开发 web 项目时, 避免不了各种错误处理, 包括 Http 错误以及自定义的业务逻辑错误.
在 NodeJS 中, 我们可以这样抛出错误:
- if (someCondition) {
- throw Error('some error')
- }
不那么优雅的方式
http 错误
koa 框架提供了 ctx.throw(400) 的方式, 可以让我们方便地抛出 http 错误, 但是如果你想同时返回一些有用信息怎么办? 也许你会这么做:
- ctx.status = 400
- ctx.body = {
- msg: "some params is invalid"
- }
业务逻辑错误
那么如果你是开发 Restful API server, 你肯定会需要定义若干业务逻辑错误码和说明, 比如像下面这样:
code 码 | 说明 |
---|---|
0 | ok |
-1 | 服务器错误 |
4001 | token 过期 |
在 controller 层面, 你也许可以这样处理 (示例代码中大写的都是常量定义, 之后不再赘述):
- router.get('/', (ctx, next) => {
- if (tokenExpire(token)) {
- const errcode = ERROR_CODE.TOKEN_EXPIRED
- ctx.body = {
- errcode,
- msg: ERROR_MSG[errcode]
- }
- return
- }
- // do something
- })
但是如果你想在 service 层面去抛出这个错误怎么办? 这时候你也许会有 2 种处理方式:
第一种, 通过定义返回值来说明错误, 在 controller 中判断返回值再返回相应错误码, 比如:
- const somefunc = async (token) => {
- const res = await tokenExpire(token)
- if (res) {
- return false
- }
- // do something
- }
第二种, 抛出 Error, 在 controller 中 catch 住异常, 并对比 err.message 来返回相应错误码, 比如:
- const somefunc = async (token) => {
- const res = await tokenExpire(token)
- if (res) {
- throw Error(ERROR_MSG.TOKEN_EXPIRED)
- }
- // do something
- }
那么有没有更好的方式呢?
更加优雅的方式
有没有一种更优雅的方式来抛出错误呢? 答案是肯定的. 我们希望无论在哪里, 直接一行代码就可以抛出错误, 并被正确处理, 返回相应的错误码和信息.
利用 koa 中间件加上我们自定义的继承于 Error 构造器的方法便可以实现.
定义 HttpError 和 CustomError
- function CustomError (code, msg) {
- Error.call(this, '')
- this.code = code
- this.msg = msg || ERROR_MSG
- || 'unknown error'
- this.getCodeMsg = function () {
- return {
- code: this.code,
- msg: this.msg
- }
- }
- }
- util.inherits(CustomError, Error)
- function HttpError (code, msg) {
- if (Object.values(HTTP_CODE).indexOf(code) <0) {
- throw Error('not an invalid http code')
- }
- CustomError.call(this, code, msg)
- }
- util.inherits(HttpError, CustomError)
抛出错误
- router.get('/HttpError', (ctx, next) => {
- throw new HttpError(HTTP_CODE.FORBIDDEN)
- })
- const somefunc = async (token) => {
- const res = await tokenExpire(token)
- if (res) {
- throw new CustomError(CUSTOM_CODE.SOME_CUSTOM_ERROR)
- }
- // do something
- }
koa 中间件统一 catch 住 Error, 并返回相应 code,msg
- app.use((ctx, next) => {
- return next().catch((err) => {
- let code = 500
- let msg = 'unknown error'
- if (err instanceof CustomError || err instanceof HttpError) {
- const res = err.getCodeMsg()
- ctx.status = err instanceof HttpError ? res.code : 200
- code = res.code
- msg = res.msg
- } else {
- console.error('err', err)
- }
- ctx.body = {
- code,
- msg
- }
- })
- })
通过以上 3 步, 抛出异常只用一行代码就搞定.
当你需要抛出 http 错误就
throw new HttpError(HTTP_CODE.FORBIDDEN)
, 当你需要抛出业务错误码就
- throw new CustomError(CUSTOM_CODE.SOME_CUSTOM_ERROR)
- .
错误抛出后, 会统一由 koa 中间件来处理. 通过对 Error 的继承, 我们将错误细分为 http error 和业务错误, 从而可以更好地处理错误返回.
这样一来, 我们便可以在代码中去优雅地处理各种错误和异常了 ^_^
by the way
我搭了一个 koa 的脚手架 https://github.com/NeoyeElf/koa2-rest-scaffold , 里面包含本文中提到的优雅地错误处理方式. 欢迎 star :)
来源: https://juejin.im/entry/5ae5494b518825670d72dfc4