当前位置: 首页 > 后端技术 > Node.js

Koa源码解析(三)——中间件机制的实现

时间:2023-04-03 14:13:33 Node.js

摘要本系列介绍Koa框架,目前重点版本为Koav1。主要分为以下几个方面:Koa源码分析(一)——generatorKoa源码分析(二)——co实现Koa源码分析(三)——中间件机制实现Koa概述Koa是基于generator和co新一代的中间件框架,其优势主要集中在以下几个方面。中间件机制封装了request/response,context对象使用yield,方便异步编程控制流程。当忽略同步或异步时,可以使用trycatch获取程序运行中的异常(错误处理是服务器程序的核心)示例代码varKoa=require('koa');varapp=newKoa();//添加中间件1app.use(function*(next){varstart=newDate;console.log("start========1111");yieldnext;console.log("end=======1111");varms=newDate-start;console.log('%s%s-%s',this.method,this.url,ms);});//添加中间件2app.use(function*(){console.log("start========2222");this.body='HelloWorld';console.log("end========2222");});app.listen(3000);/*start========1111start=======2222end=======2222end=======1111GET/-10start=======1111start=======2222end=======2222end=======1111GET/favicon.ico-5*/来自上面的代码,我们添加了两个中间件,第一个中间件有一个入参next,通过yield来调用。通过分析输出的日志信息不难发现,先运行middelware1中yield之前的代码,然后进入middleware2运行,middleware2运行后返回middleware1,运行yield之后的代码。由于app.use的输入是一个生成器函数,如果你熟悉生成器函数,你可能会说这是把middleware2作为middleware1中的下一个参数,依次调用多个生成器函数。是的,没错,实际操作是这样的,但是koa框架是如何组织代码实现这样的调用,并且把地狱式调用的异步编程做到这么清晰的结构呢?请看下面的源码分析源码分析ApplicationinitializationfunctionApplication(){if(!(thisinstanceofApplication))returnnewApplication;this.env=process.env.NODE_ENV||'发展';this.subdomainOffset=2;//用来存放中间件,即生成器对象this.middleware=[];this.proxy=false;//获取封装的上下文对象this.context=Object.create(context);//获取封装后的请求对象this.request=Object.create(请求);//获取封装的响应对象this.response=Object.create(response);}启动服务listen(){debug('listen');//在nodenative中调用创建服务//其中callback()是创建服务的核心,具体见下文分析。constserver=http.createServer(this.callback());//启用服务监控returnserver.listen.apply(server,arguments);}添加中间件app.use=function(fn){if(!this.experimental){//es7异步函数是不允许的,//所以我们必须确保`fn`是生成器函数assert(fn&&'GeneratorFunction'==fn.constructor.name,'app.use()需要一个生成器函数');}debug('使用%s',fn._name||fn.name||'-');//在this.middleware.push(fn);中将输入的fn依次推送到中间件数组中//返回这个,这样链式调用就返回这个;};nodenativecreateserviceapp.callback=function(){if(this.experimental){console.error('ExperimentalES7AsyncFunctionsupportisdeprecated.PleaselookintoKoav2asthemiddlewaresignaturehaschanged.')}//The添加中间件是为了实现yield的链式调用,也就是组织异步调用结构,详见下面的compose//co.wrap方法将generator函数转为Promisevarfn=this.experimental?compose_es7(this.middleware):co.wrap(compose(this.middleware));变种自我=这个;if(!this.listeners('error').length)this.on('error',this.onerror);//返回节点本机请求处理函数returnfunctionhandleRequest(req,res){res.statusCode=404;varctx=self.createContext(req,res);onFinished(res,ctx.onerror);fn.call(ctx).then(functionhandleResponse(){respond.call(ctx);}).catch(ctx.onerror);}};中间件异步构造//返回一个启动函数functioncompose(middleware){returnfunction*(next){if(!next)next=noop();vari=middleware.length;//从后面遍历中间件队列,逐一获取对应的生成器对象while(i--){//将后面的生成器对象传递给前面中间件的generatorFunctionnext=middleware[i].call(this,下一个);}//返回一个yield,next指向第一个中间件的生成器returnyield*next;}}function*noop(){}这样我们就开始函数从return(generatorfunction)指向第一个中间件,然后从前一个while形成的调用链从前到后依次调用下一个中间件循环直到最后一个中间件然后返回这里。我们回到启动函数处的callback(),调用co.wrap()实现生成器函数的分步调用。