2

koa中如何优雅地处理异常

 1 year ago
source link: http://neoyeelf.github.io/2018/04/29/koa%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BC%98%E9%9B%85%E5%9C%B0%E5%A4%84%E7%90%86%E5%BC%82%E5%B8%B8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

koa中如何优雅地处理异常

2018-04-29

一个良好的编码习惯必然离不开异常处理,本文将主要介绍如何在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构造器的方法便可以实现。

1.定义HttpError和CustomError

function CustomError (code, msg) {
Error.call(this, '')
this.code = code
this.msg = msg || ERROR_MSG[code] || '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)

2.抛出错误

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

3.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,

通过以上3步,抛出异常只用一行代码就搞定。
当你需要抛出http错误throw new HttpError(HTTP_CODE.FORBIDDEN),当你需要抛出业务错误码throw new CustomError(CUSTOM_CODE.SOME_CUSTOM_ERROR)
错误抛出后,会统一由koa中间件来处理。通过对Error的继承,我们将错误细分为http error和业务错误,从而可以更好地处理错误返回。

这样一来,我们便可以在代码中去优雅地处理各种错误和异常了^_^

by the way

我搭了一个koa的脚手架,里面包含本文中提到的优雅地错误处理方式。欢迎star :)

上一篇:golang也能实现抽象类了?

下一篇:一次Node.js的性能问题排查


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK