学习Koa - 让我们写一个中间件

4,526

中间件写法

根据我们前文的分析,中间件通常为以下形式:

async (ctx, next) => {....}

一般来说中间件有自己的配置,所以我们总结出来一种通用的中间件写法,我们通过传入配置的方式可以返回根据配置定制的中间件:

// 通用中间件写法
module.exports = function(options) {
  // 配置处理
  
  return aysnc (ctx, next) => {
    // 中间件逻辑...
  }
}

使用方式:

app.use(middleware(options)) // middleware(options)返回一个中间件

除此之外,还有另一种中间件写法。正如前文分析的一样,koa-compose可以将多个中间件合成一个中间件,那么我们也可将有关联的中间件合成一个大的中间件,写法如下:

app.use(compose([middleware1, middleware2, ...]))

常见中间件

Koa是基于中间件模式的,但是框架本身并不包含任何中间件。在实际的开发当中,我们可以通过组合中间件进行功能的实现。Koa中常见的中间件如下:

  • Koa-router:Koa-router的实现机制与koa本身的实现类似,都是分为两个阶段,初始化阶段和执行阶段。初始化阶段主要涉及到路由的注册,执行阶段为寻找到匹配的路由,然后使用compose函数合成一个中间件,执行该中间件。一个例子说明:
var Koa = require('koa');
var Router = require('koa-router');

var app = new Koa();
var router = new Router();

router.get('/', (ctx, next) => {}); // 注册路由

app.use(router.routes()) // 返回匹配路由的复合中间件
  .use(router.allowedMethods());

感兴趣的也可以参看源代码,简单易懂

  • Koa-bodyparser 前文中讲过,node只会对header进行解析,header解析之后便发送一个request事件,body数据获取需要自己对req(incomingMessage)上的data和end事件进行监听。
const http = require('http');

http.createServer((req , res)=>{
  console.log('request here.');
  let data = '';
  req.on('data',(chunk)=>{
    data += chunk;
  });
  req.on('end',()=>{
    console.log('data:',data);
  });
}).listen(3000,'127.0.0.1');

bodyparser中间件的作用就是获取body数据并按照想要的格式进行解析,将解析后的数据赋值给ctx.body。

app.use(bodyParser(options));
  • Koa-static:为静态资源访问创建一个服务器,根据url访问对应的文件夹、文件
  • Koa-ejs:使用ejs渲染服务端模板

更多中间件请参考koa官方清单:https://github.com/koajs/koa/wiki

自己写中间件

说了这么多中间件,我们以自己写一个中间件作为该篇结束。以下示例为一个现实中的例子,做了一部分精简。问题的背景为,网站经常被某些ip攻击,影响业务正常运行,于是做了一个中间件进行ip的过滤,对于ip黑名单上的ip一律拒绝进一步处理请求。希望通过这个示例,能够使大家进一步了解中间件的作用。

/*
** iplimiter: ip过滤中间件,对于在ip黑名单上的ip直接返回,请求不再进行下去
** ip_blacklist: Array, example: ['192.123.12.11']
*/
module.exports = function(ip_blacklist) {
  return async (ctx, next) => {
    if(!Array.isArray(configs) && configs.length) {
      let ip = ctx.request.headers['x-real-ip'] || '' //获取客户端ip,由于使用nginx作为负载均衡,所以获取ip的方式可通过x-real-ip字段
      if(ip && ip_blacklist.indexOf(ip) !== -1) {
        await next()
      } else {
        return res.end('ip restricted')
      }
    } else {
      await next()
    }
  }
}

也请大家关注我的知乎专栏,涨涨粉:知乎专栏:前端思考