当前位置: 首页 > 科技观察

深入浅出Node中间件原理

时间:2023-03-23 10:31:28 科技观察

前言中间件是介于应用系统和系统软件之间的一类软件。它利用系统软件提供的基本服务(功能)将网络上的应用系统的各个部分或不同的应用连接起来,从而达到资源共享和功能共享的目的。在NodeJS中,中间件主要是指对http请求的详细处理进行封装的方法。我们都知道在http请求中经常会涉及很多动作,如下:IP过滤查询字符串传递请求体解析cookie信息处理权限验证日志会话管理中间件(session)gzip压缩中间件(如compress)错误处理当然有还有许多自定义处理操作。对于Web应用,我们并不想了解每一个处理工作的细节,而是希望专注于业务开发,以达到提高开发效率的目的。因此引入Node中间件来简化和封装这些基本的逻辑处理细节。Node中间件本质上是让特定的过滤器在进入特定的业务处理之前进行处理。如下图所示:目前我们所见的主流nodejs框架,connect、koa、express、egg、nest等,都离不开中间件的设计理念,所以为了让大家更深入的了解纵观nodejs世界,我们会对中间件的实现原理进行大量的对比研究。了解了节点中间件的概念后,我们就手动实现中间件。最后简单分析一下koa中中间件的实现思路。文章大纲如下:nodemiddleware核心原理是实现koa中间键。koa中间件机制用于实现一个自己的koa中间件。处理请求和响应。我们在实现节点中间件模型时需要考虑的问题之一是多个中间件的共存。我们需要思考如何将多个中间件自动化执行,否则在request到response的过程中,只会执行最开始的中间件,所以我们的基本中间件形式如下:constmiddleware=(req,res,next)=>{//请求处理逻辑next()}复制代码接下来写一个简单的案例看看中间件是如何实现的//定义几个中间函数constm1=(req,res,next)=>{console.log('m1run')next()}constm2=(req,res,next)=>{console.log('m2run')next()}constm3=(req,res,next)=>{console.log('m3run')next()}//中间件集合constmiddlewares=[m1,m2,m3]functionuseApp(req,res){constnext=()=>{//获取第一个中间件constmiddleware=middlewares.shift()if(middleware){middleware(req,res,next)}}next()}//第一个请求流进入useApp()复制代码从上面的代码不难发现next的函数,它是关键参数到实现中间件链的自动调用。打印结果如下:m1runm2runm3run复制上面的代码,实现了基本中间件的执行方式,但是还要考虑异步问题,如果中间件也依赖于第三方模块或api的支持,比如验证、识别等服务。我们需要在异步中间件的回调中执行next来保证正常的调用执行顺序,如下代码所示:constm2=(req,res,next)=>{fetch('/xxxxx').then(res=>{next()})}复制代码还有一个中间件场景,比如日志中间件,请求监听中间件,它们会在业务处理前后执行相关逻辑。这时候我们就需要能够对下一个函数进行二次处理。我们可以把next的返回值包装成一个promise,这样在业务处理完成后可以通过then回调继续。处理中间件逻辑。如下:functionuseApp(req,res){constnext=()=>{constmiddleware=middlewares.shift()if(middleware){//将返回值包装成Promise对象returnPromise.resolve(middleware(req,res,next))}else{returnPromise.resolve("end")}}next()}复制代码现在我们可以使用如下方法调用:constm1=(req,res,next)=>{console.log('m1start')returnext().then(()=>{console.log('m1end')})}复制代码上面我们已经实现了一个基本的中间件设计模式,当然我们也可以使用Async和await来实现,而写法会更加优雅简洁。这是一个简单的例子:constm1=async(req,res,next)=>{//something...letresult=awaitnext();}constm2=async(req,res,next)=>{//something...letresult=awaitnext();}constm3=async(req,res,next)=>{//something...letresult=awaitnext();returnresult;}constmiddlewares=[m1,m2,m3];函数eApp(req,res){constnext=()=>{constmiddleware=middlewares.shift()if(middleware){returnPromise.resolve(middleware(req,res,next))}else{returnPromise.resolve("end")}}next()}//启动中间件useApp()复制代码在koa2框架中,中间件的实现是将next()方法的返回值封装成一个Promise对象,实现洋葱圈它提出的模型,如下图所示:koa中间件实现方法koa2框架的中间件实现原理非常优雅。我觉得很有必要研究一下。这里是核心思想:functioncompose(middleware){//提前判断中间件的类型,防止后续出错beafunctiontypeif(typeoffn!=='function')thrownewTypeError('Middlewaremustbecomposedofffunctions!')}returnfunction(context,next){//使用闭包缓存索引实现调用计数letindex=-1returndispatch(0)functiondispatch(i){//防止next()方法重复调用.length)fn=nextif(!fn)returnPromise.resolve()try{//wrappernext()返回值为Promise对象returnPromise.resolve(fn(context,dispatch.bind(null,i+1)));}catch(err){//异常处理returnPromise.reject(err)}}}}使用koa中间件机制实现自己的koa中间件。了解了中间件的设计机制和原理后,我们是不是要马上写一个中间件呢?笔者在这里给大家举个例子。在H5-Dooring项目的服务端代码中,我们需要分配用户登录权限。这时候我们提供一个统一的中间件来处理,如下代码所示://模拟数据库操作consttoken=db.user();//router或者koa的中间件接下来必须使用await来处理,否则将无法正常响应数据')if(uidArr.length>1){uid=uidArr.pop().trim()}if(token[uid]&&token[uid][1]===t){awaitnext()}else{ctx.status=403;ctx.body={state:403,msg:'您没有权限操作'}}}上面的代码实现了用户登录状态的处理,如果用户没有登录的话,请问任何一个接口需要登录,会返回权限不足或者重定向到请求库中的登录页面。那么,今天你又长知识了吗?【小编推荐】Python好,但请不要盲目使用每个项目都会用到!红帽开放混合云助力企业成为数字原生代企业分析鸿蒙系统的helloworld程序如何调用,SYS_RUN有什么作用,5G为何突然“不流行”?新方向、新特性:Python3.9完整版发布