本文转载自微信公众号《JS日报》,作者灰灰。转载本文请联系JS每日一问公众号。一、什么是中间件(Middleware)是介于应用系统和系统软件之间的一类软件。它利用系统软件提供的基本服务(功能)连接应用系统的各个部分或网络上的不同应用。可以达到资源共享、功能共享的目的。在NodeJS中,中间件主要是指对HTTP请求的详细处理进行封装的方法。比如在express、koa等web框架中,中间件的本质是一个回调函数,参数包括request对象、response对象和执行下一个中间件的函数,在这些中间件函数中,我们可以执行业务逻辑代码,修改请求和响应对象,返回响应数据等操作。2.封装koa是目前流行的基于NodeJS的web框架,本身支持的功能不多,所有功能都可以通过中间件扩展实现。通过添加不同的中间件来构建Koa应用程序,以实现不同的需求。Koa中间件采用洋葱圈模型,每次执行next中间件时,传入两个参数:ctx:封装request和response的变量next:输入下一个要执行的中间件的函数。接下来为koa封装中间件:koa的中间件是函数,可以是async函数,也可以是普通函数//asyncfunctionapp.use(async(ctx,next)=>{conststart=Date.now();awaitnext();constms=Date.now()-start;console.log(`${ctx.method}${ctx.url}-${ms}ms`);});//普通函数app.use((ctx,next)=>{conststart=Date.now();returnext().then(()=>{constms=Date.now()-start;console.log(`${ctx.method}${ctx.url}-${ms}ms`);});});接下来通过中间件封装http请求过程中常用的几个函数:){try{//verify函数验证token并获取用户相关信息awaitverify(token)}catch(err){console.log(err)}}//进入下一个中间件awaitnext()}catch(err){console.log(err)}}日志模块constfs=require('fs')module.exports=(options)=>async(ctx,next)=>{conststartTime=Date.now()constrequestTime=newDate()awaitnext()constms=Date.now()-startTime;letlogout=`${ctx.request.ip}--${requestTime}--${ctx.method}--${ctx.url}--${ms}ms`;//输出日志文件fs.appendFileSync('./log.txt',logout+'\n')}Koa中有很多第三方中间件,比如koa-bodyparser,koa-static等。我们先看看它们大致的简单实现:koa-bodyparserkoa-bodyparser中间件把我们的post请求和表单提交的query字符串转换成一个对象,挂起来在ctx.request.body上,方便我们在其他中间件或者接口取值//file:my-koa-bodyparser.jsconstquerystring=require("querystring");module.exports=functionbodyParser(){returnasync(ctx,next)=>{awaitnewPromise((resolve,reject)=>{//存放数据的数组letdataArr=[];//接收数据ctx.req.on("data",data=>dataArr.push(data));//集成Data并使用Promise成功ctx.req.on("end",()=>{//获取请求数据类型json或formletcontentType=ctx.get("Content-Type");//获取数据Buffer格式letdata=Buffer.concat(dataArr).toString();if(contentType==="application/x-www-form-urlencoded"){//如果是表单提交,将查询字符串转换成对象赋值给ctx。request.bodyctx.request.body=querystring.parse(data);}elseif(contentType==="applaction/json"){//如果是json,将字符串格式的对象转成对象赋值给ctx.request.bodyctx.request.body=JSON.parse(data);}//成功执行回调resolve();});});//继续执行awaitnext();};};koa-statickoa-static中间件的作用是在服务端收到请求的时候帮我们处理静态文件{promisify}=require("util");//将stat和access转化为Promise)=>{//将访问的路由处理成绝对路径,这里需要使用join因为可能是/letrealPath=path.join(dir,ctx.path);try{//获取stat对象letstatObj=awaitstat(realPath);//如果是文件,则设置文件类型,直接响应内容,否则按文件夹查找index.htmlif(statObj.isFile()){ctx.set("Content-Type",`${mime.getType()};charset=utf8`);ctx.body=fs.createReadStream(realPath);}else{letfilename=path.join(realPath,"index.html");//如果file不存在,执行catch中的next交给其他中间件处理awaitacess(filename);//设置f文件类型并响应内容ctx.set("Content-Type","text/html;charset=utf8");ctx.body=fs.createReadStream(filename);}}catch(e){awaitnext();}}}3.总结在实现中间件的时候,一个单一的中间件应该足够简单,职责单一,中间件的代码写法应该是高效,必要时通过缓存重复获取数据Koa本身比较简单,但是通过中间件的机制,可以实现各种需要的功能,使得Web应用具有很好的扩展性和组合性。通过将公共逻辑的处理写在中间件中,不需要在每个接口回调中都写相同的代码,减少了冗余代码。过程就像装饰器模式参考https://segmentfault.com/a/1190000017897279https://www.jianshu.com/p/81b6ebc0dd85https://baike.baidu.com/item/%E4%B8%AD%E9%97%B4%E4%BB%B6
