node.js express和koa中间件机制和错误处理机制

目录
  • 一、前言
  • 二、中间件问题解析
  • 三、错误逻辑捕获
    • 3.1 express的错误捕获逻辑
    • 3.2 同步逻辑错误获取的底层逻辑
    • 3.3 异步逻辑错误获取的底层逻辑
    • 3.4 koa的错误获取逻辑

一、前言

大家可能都知道koa是express核心原班人马写的,那么他们为什么要在express后再造一个koa的轮子呢? 今天就给大家带来一些分析。希望能够起到一个抛砖引玉的作用。

其实,这个题目也可以这么问, express有什么缺点? koa解决了一些express的什么问题? 这也在一些面试题中会这么问。所以,为了实现自己的理想(money), 志同道合的同志们可以随我分析一下了。

我想先从express的一个非常重要的特征开始说起,那就是 中间件。 中间件贯穿了express的始终,我们在express中比较常用到应用级的中间件,比如:

    const app = require('express')();
    app.use((req, res, next) => {
        // 做一些事情。。。
        next();
    })

再比如我们更常用到的路由级中间件。 我为什么要叫它是路由级的呢? 因为它的内部也同样维护着一个next

    app.get('/', (req, res, next) => {
        res.send('something content');
    })

这里中间件我不详细展开。 后面有我对中间件的详细解析,欢迎大家围观。

那么我们可以看到,其中会有个关键的next, 它在express内部做的是从栈中获取下一个中间件的关键。

那么重点来了, 我们开始研究express这里的实现会隐藏什么问题。

二、中间件问题解析

通过一个例子来看:

    const Express = require('express');
    const app = new Express();
    const sleep = () => new Promise(resolve => setTimeout(function(){resolve(1)}, 2000));
    const port = 8210;
    function f1(req, res, next) {
      console.log('this is function f1....');
      next();
      console.log('f1 fn executed done');
    }

    function f2(req, res, next) {
      console.log('this is function f2....');
      next();
      console.log('f2 fn executed done');
    }

    async function f3(req, res) {
      console.log('f3 send to client');
      res.send('Send To Client Done');
    }
    app.use(f1);
    app.use(f2);
    app.use(f3);
    app.get('/', f3)
    app.listen(port, () => console.log(`Example app listening on port ${port}!`))

理想下的返回,和真正的返回,目前是没有问题的。

    this is function f1....
    this is function f2....
    f3 send to client
    f1 fn executed done
    f2 fn executed done

好的,那么再继续下一个例子。 在下一个例子中,其它都是没有变化的,只有一个地方:

    const sleep = () => new Promise(resolve => setTimeout(function(){resolve()}, 1000))
    async function f3(req, res) {
        await sleep();
      console.log('f3 send to client');
      res.send('Send To Client Done');
    }

这时你认为的返回值顺序是什么样的呢?

可能会认为跟上面的没有变化,因为我们增加await了,照道理应该等待await执行完了,再去执行下面的代码。 其实结果并不是。

返回的结果是:

this is function f1....
    this is function f2....
    f1 fn executed done
    f2 fn executed done
    f3 send to client

发生了什么?? 大家可能有点吃惊。但是,如果深入到express的源码中去一探究竟,问题原因也就显而易见了。

具体源码我在这一篇中就不详细分析了,直接说出结论:

因为express中的中间件调用不是Promise 所以就算我们加了async await 也不管用。

那么koa中是怎么使用的呢?

const Koa = require('koa');
const app = new Koa();
const sleep = () => new Promise(resolve => setTimeout(function(){resolve()}, 1000))
app.use(async (ctx, next) => {
    console.log('middleware 1 start');
    await next();
    console.log('middleware 1 end');
});
app.use(async (ctx, next) => {
    await sleep();
    console.log('middleware 2 start');
    await next();
    console.log('middleware 2 end');
});
app.use(async (ctx, next) => {
    console.log('middleware 3 start')
    ctx.body = 'test middleware executed';
})

不出所料, 实现的顺序是:

middleware 1 start
middleware 2 start
middleware 3 start
middleware 2 end
middleware 1 end

原因是: koa 内部使用了Promise,所以能够控制顺序的执行。

综合上面的例子,我们知道了express中中间件使用的时候,如果不清楚原理,是容易踩坑的。 而koa通过使用async 和 await next() 实现洋葱模型,即:通过next,到下一个中间件,只要下面的中间件执行完成后,才一层层的再执行上面的中间件,直到全部完成。

三、错误逻辑捕获

3.1 express的错误捕获逻辑

同样,先看express在错误逻辑的捕获上有什么特点:

app.use((req, res, next) => {
    // c 没有定义
    const a = c;
});
// 错误处理中间件
app.use((err, req, res, next) => {
    if(error) {
        console.log(err.message);
    }
    next()
})
process.on("uncaughtException", (err) => {
    console.log("uncaughtException message is::", err);
})

再看一个异步的处理:

app.use((req, res, next) => {
    // c 没有定义
    try {
        setTimeout(() => {
            const a = c;
            next()
        }, 0)
    } catch(e) {
        console.log('异步错误,能catch到么??')
    }
});
app.use((err, req, res, next) => {
    if(error) {
        console.log('这里会执行么??', err.message);
    }
    next()
})
process.on("uncaughtException", (err) => {
    console.log("uncaughtException message is::", err);
})

可以先猜一下同步和异步的会不会有所区别?

答案是: 有很大的区别!!

具体分开来看:

  • 同步的时候, 不会触发 uncaughtException, 而进入了错误处理的中间件。
  • 异步的时候,不会触发错误处理中间件, 而会触发 uncaughtException

这中间发生了什么?

3.2 同步逻辑错误获取的底层逻辑

逻辑是: express内部对同步发生的错误进行了拦截,所以,不会传到负责兜底的node事件 uncaughtException ,如果发生了错误,则直接绕过其它中间件,进入错误处理中间件。 那么,这里会有一个很容易被忽略的点, 那就是,即使没有错误处理中间件做兜底,也不会进入node的 uncaughtException, 这时, 会直接报 500错误。

3.3 异步逻辑错误获取的底层逻辑

还是因为express的实现并没有把Promise考虑进去, 它的中间件执行是同步顺序执行的。 所以如果有异步的,那么错误处理中间件实际是兜不住的,所以,express对这种中间件中的异步处理错误无能为力。

从上面的异步触发例子来看, 除了错误处理中间件没有触发,我们当中的try catch也没有触发。这是一个大家可能都会踩到的坑。 这里其实是与javascript的运行机制相关了。具体原因见本篇 JavaScript异步队列进行try catch时的问题解决

所以要想去catch 当前的错误,那么就需要用 async await

app.use(async (req, res, next) => {
    try {
        await (() => new Promise((resolve, reject) => {
            http.get('http://www.example.com/testapi/123', res => {
                reject('假设错误了');
            }).on('error', (e) => {
                throw new Error(e);
            })
        }))();
    } catch(e) {
        console.log('异步错误,能catch到么??')
    }
});

这样,我们的catch不仅可以获取到, uncaughtException也可以获取到。

3.4 koa的错误获取逻辑

总体上是跟express差不多,因为js的底层处理还是一致的。但还是使用上有所差异。

上面也提过洋葱模型,特点是最开始的中间件,在最后才执行完毕,所以,在koa上,可以把错误处理中间件放到中间件逻辑最前面。

const http = require('http');
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next)=>{
    try {
        await next();
    } catch (error) {
        // 响应用户
        ctx.status = 500;
        ctx.body = '进入默认错误中间件';
        // ctx.app.emit('error', error); // 触发应用层级错误事件
    }
});
app.use(async (ctx, next) => {
    await (() => new Promise((resolve, reject) => {
        http.get('http://www.example.com/testapi/123', res => {
            reject('假设错误了');
        }).on('error', (e) => {
            throw new Error(e);
        })
    }))();
    await next();
})

上面的代码, reject出的错误信息,会被最上面的错误处理中间件捕获。总结来说,js的底层机制是一样的, 只是使用方法和细节点上不一样,大家在用的时候注意一下,

到此这篇关于node.js express和koa中间件机制和错误处理机制的文章就介绍到这了,更多相关node.js express和koa内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • node.js+express留言板功能实现示例

    目录 留言板 所需类库 开源项目 项目结构 留言板 基于nodejs+express+art-template的留言板功能.包含列表界面.添加界面和发送留言功能. 所需类库 直接copy以下package.json 然后直接 npm install 或者yarn install 即可. package.json所需内容如下. { "name": "nodejs_message_board", "version": "2021.09&qu

  • node.js利用express自动搭建项目的全过程

    一.安装express库和生成器 打开cmd输入命令:  yarn global add express express-generator 解释: 上面里两个模块分别表示库和生成器,在express3时,安装express会自动的给你安装生成器express-generator ,但是在express4时,他们就被分开了,所以需要分别安装. 安装好后可以通过命令:express --version 检查是否安装成功. 出现版本号即为安装成功(如下图所示). 二.express生成器自动创建ex

  • Node.js中Express框架的使用教程详解

    目录 Express简介 Express生成器 1. 什么是Express生成器 2. 安装Express生成器 创建Express项目 安装项目依赖 运行Express项目 Express目录结构说明 创建一个新路由模块 1. 创建ejs模块文件 2. 创建路由文件 3. 在app.js中引入路由 4. 运行项目 经过前面几天对Node.js的学习,基本的开发知识已经逐渐掌握,所谓工欲善其事必先利其器,今天进一步学习Node.js的开发框架Express.利用Express框架可以快速的进行W

  • Node.js的Koa实现JWT用户认证方法

    本文介绍了Node.js的Koa实现JWT用户认证方法,分享给大家,具体如下: 一.前置知识 基于Token的身份验证 Koajs 中文文档 Koa 框架教程 二.环境 Microsoft Visual Studio 2017集成开发环境 Node.js v8.9.4Javascript运行环境 三.开始动手,一步步来完善 1.创建基础的静态资源服务器.基础架构 以下是基本的代码,实现静态服务器,以及一个当token验证异常时候的处理. 下面我们将在这个基本代码下逐步增加注册.登录.信息的功能.

  • 如何从头实现一个node.js的koa框架

    前言 koa.js是最流行的node.js后端框架之一,有很多网站都使用koa进行开发,同时社区也涌现出了一大批基于koa封装的企业级框架.然而,在这些亮眼的成绩背后,作为核心引擎的koa代码库本身,却非常的精简,不得不让人惊叹于其巧妙的设计. 在平时的工作开发中,笔者是koa的重度用户,因此对其背后的原理自然也是非常感兴趣,因此在闲暇之余进行了研究.不过本篇文章,并不是源码分析,而是从相反的角度,向大家展示如何从头开发实现一个koa框架,在这个过程中,koa中最重要的几个概念和原理都会得到展现

  • Node.js使用Koa搭建 基础项目

    Koa 是由 Express 原班人马打造的超轻量服务端框架 与 Express 相比,除了自由度更高,可以自行引入中间件之外,更重要的是使用了 ES6 + async,从而避免了回调地狱 不过也是因为代码升级,所以 Koa2 需要 v7.60 以上的 node.js 环境 一.创建项目 手动创建一个项目目录,然后快速生成一个 package.json 文件 npm init -y 安装 koa    //当前版本 2.4.1 npm install koa -S 然后创建一个 app.js /

  • Node.js+express+socket实现在线实时多人聊天室

    本文实例为大家分享了Node.js+express+socket实现在线实时多人聊天室的具体代码,供大家参考,具体内容如下 文件结构如下: 前端部分: 登录页面Login部分: login.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="

  • Ajax 的初步实现(使用vscode+node.js+express框架)

    需求: 点击按钮 向服务端发送一个请求 然后将服务端返回的响应体结果在一个div中呈现出来 同时页面不刷新 一. 服务端的前期准备 安装node.js https://nodejs.org/en/download/ 检验是否安装成功 : 在命令行输入 node -v 按下enter 键之后出现 版本号就表明安装成功 (图如下) 使用 vscode安装express (前提是你已经成功安装了node.js) 在 vscode 里面新建一个文件夹 文件下面包含俩个文件 一个是你的前端页面代码:.ht

  • Node.js中Koa2在控制台输出请求日志的方法示例

    前言 Koa2真的是个很轻量的框架,轻量到路由都作为了模块单独了出来,Koa2也没有日志功能,如果我们需要有一些请求的日志和时间,我们就需要引入日志中间件 下面话不多说了,来一起看看详细的介绍吧 引入时间格式化库MomentJS 安装MomentJS npm install moment --save 简单格式化时间 使用YYYY-MM-DD HH:MM:SS代表 年-月-日 时-分-秒(24小时制) console.log(Moment().format('YYYY-MM-DD HH:MM:S

  • node.js学习笔记之koa框架和简单爬虫练习

    Koa -- 基于 Node.js 平台的下一代 web 开发框架 koa是由 Express 原班人马打造的,致力于成为一个更小.更富有表现力.更健壮的 Web 框架. 使用 koa 编写 web 应用,可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率.koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手.开发思路和express差不多,最大的特点就是可以避免异步嵌套.koa2利用ES7的async/await特性,极大的解决

随机推荐