常用中间件

​koa​​ 中间件的规范

  • 是⼀个​​async​​ 函数
  • 接收​​ctx​​​ 和​​next​​ 两个参数
  • 任务结束需要执⾏​​next​
const middleWare = async (ctx, next) => {  // 来到中间件,洋葱圈左边  next() // 进⼊其他中间件  // 再次来到中间件,洋葱圈右边};复制代码

中间件常⻅任务​:

  • 请求拦截
  • 路由
  • ⽇志
  • 静态⽂件服务

路由中间件

路由其实就是对策略模式的一个实现,免去了大量的 ​​if...else​​ 。

// router.jsclass Router {  constructor() {    // 策略库    this.stack = []  }  /**   * 注册策略到策略库中   * @param {*} path 请求路径   * @param {*} method 请求方法   * @param {*} middleWare 中间件   */  register(path, method, middleWare) {    let route = { path, method, middleWare }    this.stack.push(route)  }  // 注册 get 请求  get(path, middleWare) {    this.register(path, 'get', middleWare)  }  // 注册 post 请求  post(path, middleWare) {    this.register(path, 'post', middleWare)  }  // 路由中间件  routes() {    let _stack = this.stack    // 返回的是一个中间件    return async function(ctx, next) {      // 获取到上下文中的 url      let currentPath = ctx.url      // 声明一个策略      let route      // 根据上下文中的 method 查找对应的策略      for (let i = 0; i < _stack.length; i++) {        const item = _stack[i];        if (currentPath === item.path && item.method === ctx.method) {          route = item.middleWare          break        }      }      // 如果取出的策略是一个函数,执行这个函数      if (typeof route === 'function') {        route(ctx, next)        return      }      // 进入下一个中间件      await next()    }  }}module.exports = Router复制代码
// index.jsconst MyKoa = require('./myKoa')const Router = require('./router')const app = new MyKoa()const router = new Router();router.get('/index', async ctx => {  ctx.body = 'index page';});router.get('/post', async ctx => { ctx.body = 'post page'; });router.get('/list', async ctx => { ctx.body = 'list page'; });router.post('/index', async ctx => { ctx.body = 'post page'; });// 路由实例输出⽗中间件 router.routes()app.use(router.routes());app.listen(3000, () => {  console.log('????????~ sever at 3000 ~~~');})复制代码

静态⽂件服务中间件

处理静态文件的请求。

  • 配置绝对资源⽬录地址,默认为​​static​
  • 获取⽂件或者⽬录信息
  • 静态⽂件读取
  • 返回
const fs = require("fs");const path = require("path");module.exports = (dirPath = "./public") => {  return async (ctx, next) => {    if (ctx.url.indexOf("/public") === 0) {      // public开头 读取⽂件      const url = path.resolve(__dirname, dirPath);      const fileBaseName = path.basename(url);      const filepath = url + ctx.url.replace("/public", "");      console.log(filepath);      // console.log(ctx.url,url, filepath, fileBaseName)      try {        stats = fs.statSync(filepath);        if (stats.isDirectory()) {          const dir = fs.readdirSync(filepath);          // const          const ret = ['<div style="padding-left:20px">'];          dir.forEach(filename => {            console.log(filename);            // 简单认为不带⼩数点的格式,就是⽂件夹,实际应该⽤statSync            if (filename.indexOf(".") > -1) {              ret.push(                `<p><a style="color:black" href="${ctx.url                }/${filename}">${filename}</a></p>`              );            } else {              // ⽂件              ret.push(                `<p><a href="${ctx.url}/${filename}">${filename}</a></p>`              );            }          });          ret.push("</div>");          ctx.body = ret.join("");        } else {          console.log("⽂件");          const content = fs.readFileSync(filepath);          ctx.body = content;        }      } catch (e) {        // 报错了 ⽂件不存在        ctx.body = "404, not found";      }    } else {      // 否则不是静态资源,直接去下⼀个中间件      await next();    }  };};复制代码

请求拦截中间件

请求拦截应⽤⾮常⼴泛:登录状态验证、​​CORS​​ 头设置,⿊名单等。

本次实现一个⿊名单中存在的 ​​ip​​ 将被拒绝访问的功能。

module.exports = async function(ctx, next) {  const { res, req } = ctx;  const blackList = ['127.0.0.1'];  const ip = getClientIP(req);  if (blackList.includes(ip)) {//出现在⿊名单中将被拒绝    ctx.body = "not allowed";  } else {    await next();  }};function getClientIP(req) {  return (    req.headers["x-forwarded-for"] || // 判断是否有反向代理 IP    req.connection.remoteAddress || // 判断 connection 的远程 IP    req.socket.remoteAddress || // 判断后端的 socket 的 IP    req.connection.socket.remoteAddress  );}复制代码

BodyParser中间件

const middleWare = async (ctx, next) => {  console.log('????????~ body-parser');  const req = ctx.request.req  let reqData = [];  let size = 0;  await new Promise((resolve, reject) => {    req.on('data', data => {      console.log('????????~ req on', data);      reqData.push(data);      size += data.length    })    req.on('end', () => {      console.log('????????~ end');      const data = Buffer.concat(reqData, size)      console.log('????????~ data:', size, data.toString());      ctx.request.body = data.toString()      resolve();    })  })  await next()};复制代码