什么是异常

做开发的基本都知道异常,像Android开发中常见的ANR异常、空指针异常,服务器开发中经常遇到的异常404,500异常,还有一些其他常见的异常,具体可见HTTP状态码

基本上这些异常可以总结为:已知异常未知异常

已知异常就是程序中能够预想到异常,比如:服务器接口开发中某个api接口需要5个参数,而用户传递的参数多余5个或者少于5个,这种错误就是已知错误。

未知异常就说程序中不能预想到的异常,比如:服务器接口开发中遇到了空指针而程序中又没有做相应处理就会抛出HTTP状态码为500的这种异常,这种就说未知异常。

为什么需要全局异常处理

当遇到异常时如果没有全局异常处理,一般是在相应的代码逻辑中添加异常捕捉(try ... catch)或者抛出(throw)处理。

这么做其实是有弊端的:

  1. 程序代码判断逻辑过长,可读性查,不方便后期维护。
  2. 代码耦合性高,每次出现异常都需要在不同的类、文件下写异常判断逻辑。

以上只是列举的几个弊端,为了解决以上的问题程序中添加全局异常的处理就很有必要了。

这里使用的是NodeJS+Koa2开发。在Koa中,中间件是无处不在,所以这里全局异常的处理也是通过中间件的方式去实现。

如何处理

1、明确是否需要抛出异常

在服务器接口开发中需要明确是生产环境还是开发环境

生产环境中如果出现异常需要将详细的异常信息上报同时将异常状态通过api返回给客户端处理

开发环境中如果出现异常则需要将详细的异常信息在开发工具的控制台显示,同时返回将异常状态通过api返回给客户端处理。

这里的区别就说生产环境开发环境,所以通过定义一个全局变量去判断即可。由于程序中全局变量可能不止一个,为了统一声明全局变量,我们将所有的全局变量放在一个文件中,统一去加载。

新建一个config.js,里面的environment:'dev'就是对环境声明的变量,这里dev表示开发环境prod表示生产环境

module.exports = {
// prod 表示生产环境
environment:'dev',
database:{
dbName:'book',
host:'localhost',
port:3306,
user:'用户名',
password:'密码',
},
// token 设置为1天
security:{
secretKey:"密钥,要记住不能弄丢了哦",
expiresIn:60*60*24
},
host:'http://localhost:3000/'
}

配置了全局变量之后,在init.js文件的InitManager类中定义静态方法:

/**
* 加载全局配置文件
* @param {''} path 当前路径
*/
static loadConfig(path = '') {
const configPath = path || process.cwd() + '/config/config.js'
const config = require(configPath)
global.config = config
}
/**
* 加载全局异常
*/
static loadHttpException(){
const errors = require('./http-exception')
global.errs = errors
}

最后在app.js中完成初始化。

const app = new Koa()
InitManager.loadConfig()
InitManager.loadHttpException()

定义全局变量之后就需要制定返回的api异常描述

2、定义异常的返回结果

在服务器接口开发中,一个异常的返回结果,通常包含有:

  • msg:异常描述
  • errorcode:自定义的异常状态码
  • code:网络请求的状态码

两个code的区别:

  • errorcode :自定义的错误码,配合code定位具体的异常。
  • code:网络请求的状态码,如403 权限受限,而权限受限的原因有很多种,比如未登录或者登录了但是权限不足,这时候可以结合自定义的错误码和异常描述准确明确告知用户出错的原因。

定义异常描述之后就需要去判断程序是已知异常还是未知异常。

3、明确是已知异常还是未知异常

已知异常:可以定义HttpException继承Error这个类,只要是出现这异常属于HttpException都属于已知异常。

http-exception.js

/**
* 默认的异常
*/
class HttpException extends Error{
constructor(msg='服务器异常',errorCode=10000, code=400){
super()
this.errorCode = errorCode
this.code = code
this.msg = msg
}
} /**
* 资源未找到提示
*/
class NotFound extends HttpException{
constructor(msg, errorCode) {
super()
this.msg = msg || '资源未找到'
this.errorCode = errorCode || 10000
this.code = 404
}
} module.exports = {
HttpException,
NotFound
}

环境变量声明了、异常也做了处理,那么如何监听全局异常呢?

4、全局异常监听

首先编写捕捉异常处理中间件catchError.js

const {HttpException} = require('../core/http-exception')

const catchError = async (ctx, next)=>{
try {
await next()
} catch (error) {
// 已知异常
const isHttpException = error instanceof HttpException
// 开发环境
const isDev = global.config.environment === 'dev'
// 在控制台显示未知异常信息:开发环境 不是HttpException 抛出异常
if(isDev && !isHttpException){
throw error
} /**
* 是已知错误,还是未知错误
* 返回:
* msg 错误信息
* error_code 错误码
* request 请求的接口路径
*/
if(isHttpException){
ctx.body = {
msg:error.msg,
error_code:error.errorCode,
request:`${ctx.method} ${ctx.path}`
}
ctx.status = error.code
}
else{
ctx.body = {
msg: '服务器出现了未知异常',
error_code: 999,
request:`${ctx.method} ${ctx.path}`
}
ctx.status = 500
}
}
} module.exports = catchError

然后在app.js中加载中间件

const catchError = require('./middlewares/exception')
const app = new Koa()
// 全局异常中间件监听、处理,放在所有中间件的最前面
app.use(catchError)
app.listen(3000)

以上就完成了全局异常的处理,接下来就是如何使用这个全局异常了。

5、出现异常后及时抛出异常

这里以资源未找到为例。在查询数据库中,有的时候会出现数据找不到情况,这是用就可以抛出资源未找到的异常。

models/book.js

 /**
* 获取书籍详情
* @param {书籍id} bid
*/
static async detail(bkid) {
const book =await Book.findOne({
where: {
bkid
}
})
if (!book) {
// 通过全局异常的方式,抛出资源未找到的错误
throw new global.errs.NotFound()
}
return book
}

咨询请加微信:轻撩即可。

全栈项目|小书架|服务器开发-Koa2 全局异常处理的更多相关文章

  1. 全栈项目|小书架|服务器开发-Koa2中间件机制洋葱模型了解一下

    KOA2 是什么? Koa是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小.更富有表现力.更健壮的基石. 通过利用 asyn ...

  2. 全栈项目|小书架|服务器开发-Koa2 连接MySQL数据库(Navicat+XAMPP)

    为什么使用数据库 为什么需要数据库?-知乎 相比与文件系统,数据库具有以下优势: 高效率:查找效率高 高可用:可数据库共享 安全性强:数据不能随意修改 选择哪个数据库 数据库可以分为关系型数据库和非关 ...

  3. 全栈项目|小书架|服务器开发-Koa2 参数校验处理

    为什么需要做参数校验 在开发中,无论是App开发还是服务器接口开发, 我们无法去预测用户传入的数据,因此参数(数据)校验是开发中不可或缺的一环. 例如像App的注册登录表单提交页面,就要做好多层的判断 ...

  4. 全栈项目|小书架|服务器开发-Koa全局路由实现

    什么是路由 路由就是具体的访问路径,指向特定的功能模块.一个api接口是由ip(域名)+端口号+路径组成,例如 :https://www.npmjs.com/package/koa-router就是一 ...

  5. 全栈项目|小书架|服务器开发-NodeJS 使用 JWT 实现登录认证

    通过这篇 全栈项目|小书架|服务器开发-JWT 详解 文章我们对JWT有了深入的了解,那么接下来介绍JWT如何在项目中使用. 安装 $ npm install jsonwebtoken 生成 Toke ...

  6. 全栈项目|小书架|服务器开发-NodeJS 项目分包

    唠嗑 参考的是慕课网七月老师的课程,七月的课质量真的挺高的,推荐一波.这次的小书架项目源码不会全部公开,因为用了七月老师课程的绝大部分代码.虽然代码不全,但是只要思路看得懂,代码实现就很简单了. 小书 ...

  7. 全栈项目|小书架|服务器开发-JWT 详解

    JWT 官方简介:Introduction to JSON Web Tokens 文章基本是官网内容的翻译,英文不错的同学可点击上面的链接直接看英文文档. 什么是 JWT JWT全称是JSON Web ...

  8. 全栈项目|小书架|服务器开发-NodeJS 中使用 Sequelize 操作 MySQL数据库

    安装 官网:https://sequelize.org/v5/manual/getting-started.html 安装sequelize及数据库连接驱动 npm install --save se ...

  9. 全栈项目|小书架|服务器端-NodeJS+Koa2实现首页图书列表接口

    通过上篇文章 全栈项目|小书架|微信小程序-首页水平轮播实现 我们实现了前端(小程序)效果图的展示,这篇文章来介绍服务器端的实现. 首页书籍信息 先来回顾一下首页书籍都有哪些信息: 从下面的图片可以看 ...

随机推荐

  1. MySQLdb操作数据库

    堡垒机前戏 开发堡垒机之前,先来学习Python的paramiko模块,该模块机遇SSH用于连接远程服务器并执行相关操作 SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接: + ...

  2. LeetCode_682-Baseball Game

    给定一个字符串列表,字符串包含整数,’+’,’D’,’C’,整数代表一个分数,’+’代表后两个有效分数的和,’D’代表后一个有效分数的两倍,’C’代表删除后一个有效的分数值,最后求所有有效分数的和.例 ...

  3. numpy.rollaxis函数

    numpy.rollaxis numpy.rollaxis 函数向后滚动特定的轴到一个特定位置,格式如下: numpy.rollaxis(arr, axis, start) 参数说明: arr:数组 ...

  4. react-native开发经验

    # **RN开发经验** ## 一.环境配置关于环境配置,前辈已有完整的总结:http://tvrn.devops.letv.com/docs/Environment.html **IDE准备:** ...

  5. 设计模式常见面试知识点总结(Java版)

    设计模式 这篇总结主要是基于我设计模式系列的文章而形成的的.主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点.谢谢 更多详细内容可以到我的cdsn博客上查看: https:// ...

  6. sql 外键 on update cascade 和 on delete cascade 作用区别?

    这是数据库外键定义的一个可选项,用来设置当主键表中的被参考列的数据发生变化时,外键表中响应字段的变换规则的.update 则是主键表中被参考字段的值更新,delete是指在主键表中删除一条记录:on ...

  7. Redis开发与运维:数据迁移(下)

    上一篇,有朋友留言redis-port,借此机会我尝试使用一下redis-port这个同步工具 redis-port 已编译版 https://github.com/CodisLabs/redis-p ...

  8. 帝国CMS 6.5功能解密:网站安全防火墙使用说明

    有关帝国CMS新版防火墙介绍可以查看:http://bbs.phome.net/showthread-13-136169-0.html 本文为大家讲解如何使用网站防火墙:一.配置“网站防火墙”有下面两 ...

  9. Qt5教程: (2) 信号与槽

    1. 新建工程 新建一个"Qt Widgets Application"工程 2. 添加按钮 一个Qt工程会有很多个控件, 如果把逻辑代码都写在main函数里, main函数会非常 ...

  10. MyEclipse10 使用JRebel实现热部署

    MyEclipse10 使用JRebel实现热部署 Window --Preferences-Tomcat 6.x-JDK-JVM -noverify -javaagent:D:\JRebel\jre ...