connect
前言
connect
是一个基于http服务器的工具集,提供了一种新的组织代码的方式来与请求、响应对象进行交互,称之为中间件
!! 这里中间件,其实就是对req
以及res
进行拦截然后支持不同的中间件对其进行需求业务的追加!!!
connect的组成
首先,先看 的一张图:
connect
库对外暴露也就只有一个方法: createServer,关于这个connect
库的一个组成结构图如下:
关于connect
不能仅仅局限于只会简单的使用, 止自己只了解如何使用,而不清楚其中的原理,防止自己只见数目不见森林!!
将一一阐述!!
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); // 监听端口,在未来的回调监听中调用
这里有两个疑问:connect
是如何做到多个中间件按照使用的顺序来依次调用的?它的next
函数的设计理念是怎样的?
const app = connect(); 这里创建一个app函数,该函数的定义如下
function app(req, res, next){ app.handle(req, res, next); }
然后在这个app对象中添加
route字符串
、stack数组
属性,最终将这个app函数地址返回出来;app.use(route, (req, res, next) => { ...相关调用; next(); }) 使用中间件,调用app对象的use方法,将函数对象(这里称之为fn),与route进行捆绑,然后合并为一单独对象,添加到
stack数组
中;http.createServer(app).listen(port); 设置端口监听,并监听每一个客户端请求,也就是我们通过
connect
来创建出来的函数是一个作为每一个客户端连接的回调函数, 第2步中的fn,其实是每一个连接进来的客户端的 监听,通常情况下,我们简单是通过对connectListener
进行的监听并返回响应的操作,如果这里像面向过程那种方式来编写的话,那么编写出来的代码将会是一坨一坨的,难以维护,就算是对 整个过程进行抽离,在完成了原本的工作之后,假如想要再追加一些额外的工作,就会发现需要改造之前已经完成的框架,需要调整里面具体各个模块的代码, 了这个中间件的机制了之后, 就可以将从原本单一的回调,调整为按照原先已经设置好的中间件的顺序,当一个中间件执行完成后,自动调用下一个中间件;app.handle(req, res, next); 在触发了
requestListener
事件时,就会对应调用app.handle()方法,并传递对应的参数, 在这里就调用了next()
方法,而这里的next()
方法,则是声明定义在对应的handle函数中中的,该方法简而言之,就是不断地从app.stack
数组中取出一个fn来执行,并且在执行的 过程中,继续将这个next作为参数给传递进来,使得下一个fn能够在执行自身的逻辑代码之后,继续往下执行下一个fn函数!!
最终附带上整个库的一个执行过程:
自定义中间件
这边针对学习的关于
connect
中间件的学习,整理了一下两个步骤
- 定义一函数,该函数接收参数
req
、res
、next
,通过对原始的req、res对象的处理,完成中间件自身业务逻辑的实现;- 在实现自身逻辑之后,调用
next()
方法,完成对中间件的定义!!
自定义中间件库的实现
这边根据对中间件库的学习,模仿实现了以下的自定义中间件库: ```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 |
额外再补充多一个其他的有效的中间件库
从connect中学习到的知识
- next()函数设计的精髓,我们在定义中间件也好,使用中间件也好,我们根本没有定义关于这个next()方法的实现,都只是简单的调用next()函数,而且在从stack中取出的每一个fn函数的时候,就已经将next函数作为
每个fn的参数来调用fn函数,这样子每一个fn函数在执行之后,只需要简单调用next()函数,就能够实现
链式调用
的效果了! - 定义的每一个中间件中避免调用同步操作,因为任何的同步操作,将会阻塞其他中间件的正常安排执行!