connect

前言

connect是一个基于http服务器的工具集,提供了一种新的组织代码的方式来与请求、响应对象进行交互,称之为中间件!! :confused: 这里中间件,其实就是对req以及res进行拦截然后支持不同的中间件对其进行需求业务的追加!!!

connect的组成

首先,先看 :point_down: 的一张图: connect的结构

:trollface: connect库对外暴露也就只有一个方法: createServer,关于这个connect库的一个组成结构图如下: connect几个模块之间的关系

:warning: 关于connect不能仅仅局限于只会简单的使用, :u7981: 止自己只了解如何使用,而不清楚其中的原理,防止自己只见数目不见森林!! :point_down: 将一一阐述!!

connect的工作过程

const connect = require('connect');
const http = require('http');
const path = require('path');
const serveStatic = require('serve-static');
const app = connect();
const staticServer = serveStatic(path.join(__dirname, '/images'));  //创建一个中间件
app.use(staticServer);  //将中间件插入
http.createServer(app).listen(3000);  // 监听端口,在未来的回调监听中调用

:confused: 这里有两个疑问:connect是如何做到多个中间件按照使用的顺序来依次调用的?它的next函数的设计理念是怎样的?

  1. const app = connect(); 这里创建一个app函数,该函数的定义如下

    function app(req, res, next){ app.handle(req, res, next); }
    

    然后在这个app对象中添加route字符串stack数组属性,最终将这个app函数地址返回出来;

  2. app.use(route, (req, res, next) => { ...相关调用; next(); }) 使用中间件,调用app对象的use方法,将函数对象(这里称之为fn),与route进行捆绑,然后合并为一单独对象,添加到stack数组中;

  3. http.createServer(app).listen(port); 设置端口监听,并监听每一个客户端请求,也就是我们通过connect来创建出来的函数是一个作为每一个客户端连接的回调函数, :point_up: 第2步中的fn,其实是每一个连接进来的客户端的 监听,通常情况下,我们简单是通过对connectListener进行的监听并返回响应的操作,如果这里像面向过程那种方式来编写的话,那么编写出来的代码将会是一坨一坨的,难以维护,就算是对 整个过程进行抽离,在完成了原本的工作之后,假如想要再追加一些额外的工作,就会发现需要改造之前已经完成的框架,需要调整里面具体各个模块的代码, :u6709: 了这个中间件的机制了之后, 就可以将从原本单一的回调,调整为按照原先已经设置好的中间件的顺序,当一个中间件执行完成后,自动调用下一个中间件;

  4. app.handle(req, res, next); 在触发了requestListener事件时,就会对应调用app.handle()方法,并传递对应的参数, :stars: 在这里就调用了next()方法,而这里的next()方法,则是声明定义在对应的handle函数中中的,该方法简而言之,就是不断地从app.stack数组中取出一个fn来执行,并且在执行的 过程中,继续将这个next作为参数给传递进来,使得下一个fn能够在执行自身的逻辑代码之后,继续往下执行下一个fn函数!!

:stars: 最终附带上整个库的一个执行过程: connect的工作过程

自定义中间件

这边针对学习的关于connect中间件的学习,整理了一下两个步骤

  1. 定义一函数,该函数接收参数reqresnext,通过对原始的req、res对象的处理,完成中间件自身业务逻辑的实现;
  2. 在实现自身逻辑之后,调用next()方法,完成对中间件的定义!!

自定义中间件库的实现

:stars: 这边根据对中间件库的学习,模仿实现了以下的自定义中间件库: ```javascript /*

  • @desc: 自定义简易中间件库
  • @author: 郑耿林
  • @date: 2022-10-29 */ const EventEmitter = require('node:events'); const myEmitter = new EventEmitter();

const app = (res, next) => {}; app.stack = []; app.use = fn => { app.stack.push(fn); } app.handle = (res, out) => { let index = 0; function next(){ let fn = app.stack[index++]; if(fn){ fn(res, next); } } next(); } //! -----------我是靓丽的分割线----------- app.use((res, next) => { console.info('我是第一个中间件'); next(); }); app.use((res, next) => { console.info('我是第二个中间件'); }); myEmitter.on('my-event', (res) => { app.handle(res); }); setTimeout(() => { myEmitter.emit('my-event', { mm: 'mm', nn: 'nn' }); }, 2000); ``` 自定义中间件库的执行结果

开源中间件(官方链接)

connect中提供了一系列的开源稳定的中间件,供我们在更加方便地来使用中间件来快速搭建一个http服务!!

中间件 描述 链接地址
body-parser 提供对请求体、json、url编码的中间件 https://www.npmjs.com/package/body-parser
compression 前置压缩 https://www.npmjs.com/package/compression
connect-timeout 链接超时中间件 https://www.npmjs.com/package/connect-timeout
cookie-parser cookie解析器 https://www.npmjs.com/package/cookie-parser
cookie-session session会话中间件 https://www.npmjs.com/package/cookie-session
csurf
errorhandler 异常处理中间件 https://www.npmjs.com/package/errorhandler
express-session session会话中间件 https://www.npmjs.com/package/express-session
method-override 方法重载中间件 https://www.npmjs.com/package/method-override
morgan 日志中间件 https://www.npmjs.com/package/morgan
response-time 响应时间记录中间件 https://www.npmjs.com/package/response-time
serve-favicon 网站的favicon图标中间件 https://www.npmjs.com/package/serve-favicon
serve-index 提供包含给定路径的目录列表页面 https://www.npmjs.com/package/serve-index
serve-static 静态资源映射中间件 https://www.npmjs.com/package/serve-static
vhost 域名中间件 https://www.npmjs.com/package/vhost

:stars: 额外再补充多一个其他的有效的中间件库

从connect中学习到的知识

  1. next()函数设计的精髓,我们在定义中间件也好,使用中间件也好,我们根本没有定义关于这个next()方法的实现,都只是简单的调用next()函数,而且在从stack中取出的每一个fn函数的时候,就已经将next函数作为 每个fn的参数来调用fn函数,这样子每一个fn函数在执行之后,只需要简单调用next()函数,就能够实现链式调用的效果了!
  2. :warning: 定义的每一个中间件中避免调用同步操作,因为任何的同步操作,将会阻塞其他中间件的正常安排执行!

results matching ""

    No results matching ""