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

Koa系列——Koa中间件机制解析

时间:2023-04-03 23:40:21 Node.js

上一篇讲了如何编写自己的Koa中间件。本文将根据原理实现一个简单的中间件处理函数,并编写Koa中间件处理函数compose函数源码分析。1、compose功能实现简单。Koa中间件采用中间件洋葱模型。具体原理可以看howtowriteyourkoamiddleware。本质是嵌套执行中间件:functionmiddleware0(){console.log('middleware0')}functionmiddleware1(){console.log('middleware1')}//嵌套执行两个中间件middleware0(middleware1())当然,其实比较复杂,还要考虑中间件的异步执行,以及如何嵌套中间件。Koa中的异步处理在Koa1中使用generator+co.js,在Koa2中使用async/await。这次我们使用async/await来处理异步。可以通过将中间件作为参数传递来实现中间件的嵌套。基于此,我们进一步处理上面的代码:ps:Node7.6+支持async/awaitasyncfunctionmiddleware0(next){console.log('middleware0')awaitnext()}asyncfunctionmiddleware1(next){console.log('middleware1')}//嵌套两个中间件执行middleware0(middleware1)Koa通过compose函数处理中间件。compose函数参数是一个中间件数组,中间件数组的成员是通过use方法添加的中间件。下面写一个简单的compose函数,实现多个中间件的处理:console.log('middleware1')awaitnext()console.log('middleware1end')}asyncfunctionmiddleware2(next){console.log('middleware2')awaitnext()console.log('middleware2end')}/***@param{Array}middlewarearray*/functioncompose(middleware){//从第一个中间件开始执行returndispatch(0)functiondispatch(i){//获取i中间件fn=middleware[i]//如果没有找到中间件,直接返回到最后if(!fn)return//执行第i个中间件,传入第i+1个中间件returnfn(()=>dispatch(i+1))}}//执行compose([middleware0,middleware1,middleware2])2.源码分析我们简单的实现了一个compose功能,现在来看看源码在Koa中的实现。Koa中的compose函数已经被提取到koa-compose包中,核心代码如下:/***@param{Array}middlewarearray*@return{Function}*/functioncompose(middleware){//判断是否为数组则抛出异常if(!Array.isArray(middleware))thrownewTypeError('Middlewarestackmustbeanarray!')//判断中间件数组中的中间件是否为函数,如果不是函数则抛出异常for(constfnofmiddleware){if(typeoffn!=='function')thrownewTypeError('Middlewaremustbecomposedoffunctions!')}/***这里不执行中间件,直接返回函数*外面统一异常判断,然后开始执行中间件*/returnfunction(context,next){letindex=-1//从第一个开始执行middlewarereturndispatch(0)functiondispatch(i){//同一个中间件多次调用next并抛??出异常if(i<=index)returnPromise.reject(newError('next()calledmultipletimes'))index=i//获取第i个中间件letfn=middleware[i]/***中间件执行结束,检查是否有next回调函数传入*这个next不是中间件执行的下一个参数*/if(i===middleware.length)fn=next/***所有返回都是Promise对象*Promise对象可以保证中间件和返回请查找对象之间的执行顺序*/if(!fn)returnPromise.resolve()try{//执行第i个中间件,传入第i+1个中间件returnPromise.resolve(fn(context,dispatch.绑定(空,我+1)));}catch(err){returnPromise.reject(err)}}}}通过分析,我们可以发现源码比我们的实现更健壮:更完整的异常处理是在执行前统一检查传入的参数。多次执行下一个函数抛出异常等,最终返回结果为Promise,保证Koa中中间件和整个处理函数的执行顺序。具体可以参考如下Koa源码片段:/***application.js*fnMiddleware(ctx)是compose函数返回的函数,默认不传入next参数*Promise保证执行顺序中间件和handleResponse*/fnMiddleware(ctx).then(handleResponse).catch(onerror)3.总结从一开始写Koa中间件到现在看compose函数源码,Koa中间件机制并不复杂。了解之后,我们可以使用和编写更合适的中间件来构建自己的Koa应用。