浅析洋葱模型

开发 前端
使用过koa的小伙伴们都应该对洋葱模型有所了解,koa的独特的中间件流程控制就是通过洋葱模型来实现的,那么洋葱模型是什么,又是如何实现的呢?

 [[407985]]

前言

使用过koa的小伙伴们都应该对洋葱模型有所了解,koa的独特的中间件流程控制就是通过洋葱模型来实现的,那么洋葱模型是什么,又是如何实现的呢?

洋葱模型介绍

介绍

上图是洋葱模型比较经典的一个图,通过这个图可以看到,洋葱模型就像一个洋葱一个,是分成多层的,而一个请求进入的时候,会从外到内依次经过每一层,到最内侧之后又从内到外依次经过每一次,而我们就可以在这每一层上面做自己需要做的操作。

比如一个请求,在koa接收到请求的时候,首先需要鉴权,然后需要对请求参数解析等等,完成请求之后,需要处理异常,添加请求头等等操作,而这些操作就可以放到洋葱模型的每一层上面做处理。

示例代码

如下代码为koa的一段示例代码:

const Koa = require('koa'); 
const app = new Koa(); 
 
app.use(async (ctx, next) => { 
  console.log(1); 
  await next(); 
  console.log(2); 
}); 
 
app.use(async (ctx, next) => { 
  console.log(3); 
  await next(); 
  console.log(4); 
}); 
app.use(async (ctx, next) => { 
  console.log(5); 
  await next(); 
  console.log(6); 
}); 
 
app.listen(8000); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

执行上面的代码,会发现输出的数字顺序为1,3,5,6,4,2,与上面介绍的洋葱模型的执行顺序是一致的。

洋葱模型实现

首先我们先分析一下上面的代码app.use传入了一个异步的函数,而且app.use可以被使用多次,而这些函数在请求进入的时候会依次被调用,这是不是与发布订阅者模式是一致的,那首先我们来实现一个app.use

实现一个app.use

export interface Middleware { 
  (...rest: any): Promise<any>; 

 
export default class Onion { 
  middlewares: Middleware[] = []; 
  constructor(middlewares: Middleware[] = []) { 
    this.middlewares = middlewares; 
  } 
 
  use(middleware: Middleware) { 
    this.middlewares.push(middleware); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

上面代码我们定义了一个Onion类,通过这个类我们就可以将订阅函数进行收集,如下代码所示

const onion = new Onion() 
onion.use(async(params, next)=> { 
   console.log(1) 
   await next() 
   console.log(2) 
}) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

发布者在收集完订阅函数后需要有触发的时机,这时候就需要再给Onion添加一个执行函数

完善Onion

有小伙伴想到发布订阅者的实现代码,可能就会想到这样做:

export default class Onion { 
  middlewares: Middleware[] = []; 
  constructor(middlewares: Middleware[] = []) { 
    this.middlewares = middlewares; 
  } 
 
  use(middleware: Middleware) { 
    this.middlewares.push(middleware); 
  } 
 
  execute(params: any) { 
    this.middlewares.forEach(fn => { 
          fn(params) 
       }) 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

但是这样做的话,就无法满足洋葱模型的先入后出的顺序,那我们应该怎么做呢?

1.定义 compose函数

function compose(middlewares: Array<Middleware>) { 
if (!Array.isArray(middlewares)) { 
 throw new Error('中间件必须是数组'); 

for (let i = 0; i < middlewares.length; i++) { 
 if (typeof middlewares[i] !== 'function') { 
   throw new Error('中间件的每一项都必须是函数'); 
 } 

 
return (params: any) => { 
 let index = 0; 
 function dispatch(fn: Middleware | undefined) { 
   if (!fn) { 
     return Promise.resolve(); 
   } 
   const next = () => dispatch(middlewares[++index]); 
   return Promise.resolve(fn(params, next)); 
 } 
 return dispatch(middlewares[index]); 
}; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

2.实现execute

export default class Onion { 
middlewares: Middleware[] = []; 
constructor(middlewares: Middleware[] = []) { 
 this.middlewares = middlewares; 

 
use(middleware: Middleware) { 
 this.middlewares.push(middleware); 

 
execute(params: any) { 
 const fn = compose(this.middlewares); 
 return fn(params); 


  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

通过定义componse函数,可以将中间件函数依次按照顺序来执行。

const onion = new Onion(); 
onion.use(async (params: anynext: Middleware) => { 
 console.log(1); 
 await next(); 
 console.log(2); 
}); 
 
onion.use(async (params: anynext: Middleware) => { 
 console.log(3); 
 await next(); 
 console.log(4); 
}); 
 
onion.use(async (params: anynext: Middleware) => { 
 console.log(5); 
 await next(); 
 console.log(6); 
}); 
 
onion.execute({}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

这样我们就实现了一个简易版的洋葱模型。

本文转载自微信公众号「前端有的玩」,可以通过以下二维码关注。转载本文请联系前端有的玩公众号。

 

责任编辑:武晓燕 来源: 前端有的玩
相关推荐

2022-01-02 09:29:37

模型洋葱Koa

2020-11-02 08:51:57

中间件和洋葱模型

2011-06-20 15:13:08

Qt 对象模型

2020-09-23 14:20:07

Kubernetes容器网络模型

2022-10-25 08:01:17

洋葱模型Koa

2023-07-30 15:14:19

Koa葱圈模型

2022-08-28 20:50:29

算法模型机器学习

2017-07-07 16:36:28

BIOIO模型 NIO

2009-09-15 10:12:37

LINQ To SQL

2021-03-17 08:12:03

架构Dotnet洋葱

2012-01-17 17:21:24

JavaSwing

2010-01-25 14:18:46

C++对象模型

2011-05-24 11:20:53

OTNWSSFOADM

2024-09-09 07:46:16

2009-07-21 14:32:51

ASP.NET进程模型

2022-10-27 16:01:41

AbilityStage模型FA模型

2014-08-05 13:46:36

2022-10-08 09:18:19

架构模型

2018-07-31 09:30:44

Linux服务器网络

2022-08-15 14:49:12

物联网数据模型存储
点赞
收藏

51CTO技术栈公众号