文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

深入浅出Node中间件原理

2024-12-03 18:11

关注

 前言

中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。

在NodeJS中,中间件主要是指封装http请求细节处理的方法。我们都知道在http请求中往往会涉及很多动作, 如下:

当然还有很多自定义的处理动作. 对于Web应用而言,我们并不希望了解每一个细节性的处理工作,而是希望能够把主要精力集中在业务的开发上,以达到提升开发效率的目的, 所以引入了Node中间件来简化和封装这些基础逻辑处理细节.

node中间件本质上就是在进入具体的业务处理之前,先让特定过滤器处理。如下图所示:

我们目前看到的主流nodejs框架, 比如connect, koa, express, egg, nest等, 都离不开中间件的设计概念, 所以为了能让大家更深入的窥探nodejs世界, 我们就非常有比较研究中间件的实现原理.

正文

在了解node中间件的概念之后, 我们就来手动实现一下中间件, 最后我们会简单分析一下koa中中间件的实现思路. 文章大纲如下:

node中间件核心原理实现

由上文介绍可知中间件是从http请求开始到响应结束过程中的处理逻辑,通常需要对请求和响应进行处理. 我们在实现node中间件模式时还需要考虑的一个问题就是多中间件共存的问题, 我们要思考如何将多个中间件的执行自动化, 不然在请求到响应的过程中只会执行最开始的中间件, 所以我们基本的中间件形式如下:

  1. const middleware = (req, res, next) => { 
  2.   // 请求处理逻辑 
  3.   next() 
  4. 复制代码 

接下来我们先写个简单的案例来看看中间件是如何实现的.

  1. // 定义几个中间间函数 
  2. const m1 = (req, res, next) => { 
  3.   console.log('m1 run'
  4.   next() 
  5.  
  6. const m2 = (req, res, next) => { 
  7.   console.log('m2 run'
  8.   next() 
  9.  
  10. const m3 = (req, res, next) => { 
  11.   console.log('m3 run'
  12.   next() 
  13.  
  14. // 中间件集合 
  15. const middlewares = [m1, m2, m3] 
  16.  
  17. function useApp (req, res) { 
  18.   const next = () => { 
  19.     // 获取第一个中间件 
  20.     const middleware = middlewares.shift() 
  21.     if (middleware) { 
  22.       middleware(req, res, next
  23.     } 
  24.   } 
  25.   next() 
  26.  
  27. // 第一次请求流进入 
  28. useApp() 
  29. 复制代码 

由以上代码我们就不难发现next的作用了, 也就是实现自动调用中间件链的关键参数. 打印结果如下:

  1. m1 run 
  2. m2 run 
  3. m3 run 
  4. 复制代码 

以上即实现了基本中间件的执行模式, 但是我们还需要考虑异步的问题, 如果中间件还依赖第三发模块或者api的支持, 比如验证, 识别等服务, 我们需要在该异步中间件的回调里执行next, 才能保证正常的调用执行顺序, 如下代码所示:

  1. const m2 = (req, res, next) => { 
  2.   fetch('/xxxxx').then(res => { 
  3.     next() 
  4.   }) 
  5. 复制代码 

还有一种中间件场景, 比如说日志中间件, 请求监控中间件, 它们会在业务处理前和处理后都会执行相关逻辑, 这个时候就要求我们需要能对next函数进行二次处理, 我们可以将next的返回值包装成promise, 使得其在业务处理完成之后通过then回调来继续处理中间件逻辑. 如下所示:

  1. function useApp (req, res) { 
  2.   const next = () => { 
  3.     const middleware = middlewares.shift() 
  4.     if (middleware) { 
  5.       // 将返回值包装为Promise对象 
  6.       return Promise.resolve(middleware(req, res, next)) 
  7.     }else { 
  8.       return Promise.resolve("end"
  9.     } 
  10.   } 
  11.   next() 
  12. 复制代码 

此时我们就能使用如下方式调用了:

  1. const m1 = (req, res, next) => { 
  2.   console.log('m1 start'
  3.   return next().then(() => { 
  4.     console.log('m1 end'
  5.   }) 
  6. 复制代码 

以上我们就实现了一个基本可以的中间件设计模式, 当然我们也可以用async和await实现, 写法会更优雅和简单. 笔者这里上一份简单的例子:

  1. const m1 = async (req, res, next) => { 
  2.     // something... 
  3.     let result = await next(); 
  4.   } 
  5.    
  6.   const m2 = async (req, res, next) => { 
  7.     // something... 
  8.     let result = await next(); 
  9.   } 
  10.   const m3 = async (req, res, next) => { 
  11.     // something... 
  12.     let result = await next(); 
  13.     return result; 
  14.   } 
  15.  
  16. const middlewares = [m1, m2, m3]; 
  17.  
  18. function useApp (req, res) { 
  19.     const next = () => { 
  20.       const middleware = middlewares.shift() 
  21.       if (middleware) { 
  22.         return Promise.resolve(middleware(req, res, next)) 
  23.       }else { 
  24.         return Promise.resolve("end"
  25.       } 
  26.     } 
  27.     next() 
  28.   } 
  29. // 启动中间件 
  30. useApp() 
  31. 复制代码 

在koa2框架中, 中间件的实现方式也是将next()方法返回值封装为Promise对象,实现了其提出的洋葱圈模型,如下图所示:

koa中间件实现方式

koa2框架的中间件实现原理很优雅,笔者觉得很必要研究一下, 这里展示一下其核心思路:

  1. function compose (middleware) { 
  2.   // 提前判断中间件类型,防止后续错误 
  3.   if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!'
  4.   for (const fn of middleware) { 
  5.     // 中间件必须为函数类型 
  6.     if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!'
  7.   } 
  8.   return function (context, next) { 
  9.     // 采用闭包将索引缓存,来实现调用计数 
  10.     let index = -1 
  11.     return dispatch(0) 
  12.     function dispatch (i) { 
  13.       // 防止next()方法重复调用 
  14.       if (i <= indexreturn Promise.reject(new Error('next() called multiple times')) 
  15.       index = i 
  16.       let fn = middleware[i] 
  17.       if (i === middleware.length) fn = next 
  18.       if (!fn) return Promise.resolve() 
  19.       try { 
  20.         // 包装next()返回值为Promise对象 
  21.         return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); 
  22.       } catch (err) { 
  23.         // 异常处理 
  24.         return Promise.reject(err) 
  25.       } 
  26.     } 
  27.   } 

利用koa中间件机制实现一个自己的koa中间件

学习了中间件的设计机制和原理, 我们是不是想马上写一个中间件呢? 笔者这里给大家举一个例子. 在H5-Dooring项目的服务端代码中, 我们需要对用户登录权限进行分发, 此时我们提供统一个中间件来处理, 如下代码所示:

  1. // 模拟数据库操作 
  2. const token = db.user(); 
  3.  
  4. // router或者koa的中间件一定要用await处理next,否则将不能正常响应数据 
  5. export default async (ctx, next) => { 
  6.   const t = ctx.request.header.authorization 
  7.   let uid = ctx.request.header['x-requested-with'
  8.   let uidArr = uid.split(','
  9.   if(uidArr.length > 1) { 
  10.       uid = uidArr.pop().trim() 
  11.   } 
  12.     if(token[uid] && token[uid][1] === t) { 
  13.         await next() 
  14.     }else { 
  15.         ctx.status = 403; 
  16.         ctx.body = { 
  17.             state: 403, 
  18.             msg: '你没有权限操作' 
  19.         } 
  20.     }   

以上代码即实现用户登录态处理, 如果用户在没有登录的情况下防问任何需要登录的接口, 都将返回权限不足或则在请求库中让其重定向到登录页面.

所以, 今天你又博学了吗?

【编辑推荐】

  1. Python虽好,但请不要盲目将它用于每一个项目!
  2. 红帽开放混合云 助力企业成为数字原生企业
  3. 分析鸿蒙系统helloworld程序是如何被调用,SYS_RUN做什么事情
  4. 5G为何突然间就“不火”了?
  5. 新方向、新功能:Python3.9 完整版面世了

 

 

来源:趣谈前端内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯