ioioi

a middleware engine

Usage no npm install needed!

<script type="module">
  import ioioi from 'https://cdn.skypack.dev/ioioi';
</script>

README

ioioi:洋葱模型执行引擎

ioioi仅仅提供一个洋葱模型的执行机制。无论是前端还是后端(Node.js)都可以基于此开发自己的应用。

其核心机制只有不到80行代码。ioioi利用 async 和 await 实现,现在的前后端都是完美支持的,请尽情使用。但是如果你要用在古老的项目上,请慎重!!

安装

npm i ioioi

详细说明

这不是一个开箱即用的工具,而是一个给需要造轮子的开发者使用的组件。一般来说,在开发中会经常需要分层处理以及通过组件灵活组合的情况,无论是操作系统还是web应用。

在众多的编程语言和开发框架以及各种成型的系统上,中间件、拦截器、钩子等名词经常会出现,它们本质上没有什么区别,为了让程序易于扩展、方便维护和定制。而且实现了组件或是模块的分离,还方便大家协作。

这个扩展主要还是专注于web领域,可以用于前端,也可以用在后端,因为其简单,没有涉及到任何前后端专有的东西。

使用

const ioioi = require('ioioi');

const ioi = new ioioi();

ioi.use(async (c, next) => {
  console.log('m1 in');
  await next();
  console.log('m1 out');
});

ioi.use(async (c, next) => {
  console.log('m2 in');
  await next();
  console.log('m2 out');
});

//run是async声明的函数,你可以用在try catch中捕获错误
ioi.run();

执行结果:

m1 in
m2 in
m2 end
m1 end

执行结果是按照洋葱模型的,只是遵循的是先添加先执行的原则,因为这样更符合编码逻辑。

在之前的示例上,没有对run传递参数,实际上,run是接收一个对象作为参数的。在web请求中,总是被称为请求上下文。不过在这里,你可以按照自己的设计执行。

run是async声明的函数,你可以用在try catch中捕获错误。

传递参数

const ioioi = require('ioioi');

function context() {
    return {
        method : '',
        url : '',
        path : '',
        exec : null
    };
}

const ioi = new ioioi();

ioi.use(async (c, next) => {
  console.log('m1 in');
  await next();
  console.log('m1 out');
});

ioi.use(async (c, next) => {
  console.log('m2 in');
  await next();
  console.log('m2 out');
});

//添加第三个中间件输出传递上下文对象的信息。
ioi.use(async (c, next) => {
  console.log(c.method, c.url);
  await next();
});

let ctx = context();

ctx.method = 'GET';
ctx.path = '/';
ctx.url = '/?name=home';

//如果传递参数对象存在属性exec并且是可执行函数,则会执行。
//并且把ctx自身作为参数传递。
ctx.exec = async (c) => {
  console.log('我是最核心的应用,没有next。');
};

ioi.run(ctx);

输出结果:

m1 in
m2 in
GET /?name=home
我是最核心的应用,没有next。
m2 out
m1 out

如果传递参数对象存在属性exec并且是可执行函数,则会执行,并且会把ctx自身作为参数传递,你不必使用this来获取ctx。避免this指向错误带来的问题。

过滤执行

use原型如下:

use (midcall, filter = null) {
  //...
}

使用use添加中间件,还支持第二个参数,默认为null。如果要起作用,则需要传递一个函数对象,其主要作用是对执行过程过滤,执行时,会把中间件接收到的参数传递过去,注意只有第一个参数传递,没有next。如果函数返回false则表示不通过,不会执行此中间件。

const ioioi = require('ioioi');

function context() {
  return {
    exec : null,
    method : '',
    pass : ''
  };
}

let ioi = new ioioi();

let postCheck = (c) => {
  if (c.method == 'POST' && c.pass !== 'abc') {
    console.log('  post deny');
    return false;
  }
  return true;
};

ioi.use(async (c, next) => {
  console.log('m1 in');
  await next();
  console.log('m1 out');
});

ioi.use(async (c, next) => {
  console.log('  m2 in');
  await next();
  console.log('  m2 out');
}, postCheck);

ioi.use(async (c, next) => {
  console.log('    m3 in');
  await next();
  console.log('    m3 out');
});

let b = context();

b.method = 'POST';
b.pass = 'abcd';
b.exec = async (c) => {
  console.log('      I am b');
};

ioi.run(b);

执行结果:

m1 in
  post deny
    m3 in
      I am b
    m3 out
m1 out

可以看到,中间件m2的输出没有执行,因为POST检测没有通过,函数返回false。如果你把pass改成abc,让postCheck检测通过,则会看到如下输出:

m1 in
  m2 in
    m3 in
      I am b
    m3 out
  m2 out
m1 out

最后

简洁却极富表现力的功能,授之以渔。