npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mixin-middleware

v0.0.3

Published

Add middleware support for function calls.

Downloads

3

Readme

mixin-middleware

包装指定函数并为其添加类 koa 洋葱机制的中间件支持。

安装

yarn add mixin-middleware

# 或者使用 npm

npm install mixin-middleware

起步

使用 mixin 创建一个实例,它接收一个函数,内部会对此函数进行包装。

为行文方便,后文统一使用 action 称呼。

import mixin from 'mixin-middleware'

const action = (params) => {/* 省略实现过程 */}

// dispatcher 实例
const dispatcher = mixin({ action })

调用 dispatcher

dispatcher 是一个函数,它与 action 基本一致,不过返回值是一个 Promise。

dispatcher({/* some params */}).then((res) => {
  console.log('action 执行后的返回值:', res)
})

注册实例中间件

dispatcher.use(async (ctx, next) => {
  console.log('调用 action 前执行部分')
  await next()
  console.log('调用 action 后执行部分')
})

使用 async/await 语法等待其他中间件和 action 执行完成,在这之后处理 action 的结果。

注册局部作用中间件

使用 dispatcher.create 可以基于 dispatcher 本身再次创建一个实例。它的返回值和 action 基本一致,依然返回一个 Promise。

const scopeDispatcher = dispatcher.create()

// 注册局部作用中间件
scopeDispatcher.use('prefix', async (ctx, next) => {})
scopeDispatcher.use('suffix', async (ctx, next) => {})

// scopeDispatcher 和 dispatcher 一样,都是 action 的变体。
// 它的调用方式和 dispatcher 一致。
scopeDispatcher({/* some params */}).then((res) => {
  console.log('action 执行后的返回值:', res)
})

mixin-middleware 内部使用数组存储中间件,prefix 注册中间件时会插入到数组前面,suffix 注册中间件时会插入到数组后面。

中间件的执行顺序

// Middleware 1
dispatcher.use(async (ctx, next) => {})
// Middleware 2
dispatcher.use(async (ctx, next) => {})

// Scoped 1
scopeDispatcher.use('prefix', async (ctx, next) => {})
// Scoped 2
scopeDispatcher.use('suffix', async (ctx, next) => {})

执行顺序如下:

Scoped 1 --> Middleware 1 --> Middleware 2 --> Scoped 2 --> action --> Scoped 2 --> Middleware 2 --> Middleware 1 --> Scoped 1

Context

注册中间件时,中间件函数总共有两个参数:

  • context 对应实例的上下文。

  • next 调用下一个中间件。

context 结构如下:

  • context.req 一个数组,存储传递给 action 的参数。
  • context.res action 的返回值。
  • context.setRes 用于修改 res 使用。 原本是没有 setRes 这样一个函数,res 使用 action 的返回类型,由于类型限制,此时便不能再随意赋值了,所以我们提供了这样一个“逃生舱”。
// ctx 便是 context,这里使用缩写。
dispatcher.use(async (ctx, next) => {
  console.log('传递给 action 参数:', ctx.req)
  
  await next()
  
  console.log('action 的返回值:', ctx.res)
  
  // 修改 action 的返回值。修改 res 后,其他的中间件获取的 res 便是处理后的值了。
  ctx.setRes(...)
})

提前终止

若需要终止当前及后续中间件的执行,只需要抛出错误即可。

dispatcher.use(async (ctx, next) => {
  if(ctx.req[0] === null) {
    // 使用 throw
    throw Error('参数不能为 null')

    // 或者使用 reject
    // return Promise.reject(Error('参数不能为 null'))
  }
  await next()
})

dispatcher(null).then(() => {}).catch((err) => {
  console.log(err.message) // 参数不能为 null
})

统一错误处理

洋葱中间件确实很强大,但是我们似乎缺少了一个环节,如果 action 本身抛出错误如何处理?

那么接下来来看看 errorHandler

import mixin from 'mixin-middleware'

const action = (params) => {/* 省略实现过程 */}

// dispatcher 实例
const dispatcher = mixin({
  action,
  errorHandler(err) {
    // err 为中间件、action 所抛出的错误
    // 如果有需要,则在此对错误进行处理
    
    // 无法处理,或需要在调用处处理的错误则依旧抛出。
    throw err
  },
})

dispatcher({/* some params */})
  .then(() => {})
  .catch((err) => {
  	// 通过 Promise.catch 做错误处理
	})

示例

基本用法

import mixin from 'mixin-middleware'

function add(...numberList: number[]) {
  return numberList.reduce((res, item) => {
    if (!res) {
      return item
    }
    return res + item
  })
}

const dispatcher = mixin(add)

// 注册中间件。
// 过滤非 number 类型值
// 将最后结果乘 2
dispatcher.use(async (ctx, next) => {
  ctx.req = ctx.req.filter(
    (item) => typeof item === 'number' && !Number.isNaN(item),
  )
  await next()
  ctx.setRes((ctx.res || 0) * 2) // 将结果乘 2
})

调用结果:

// add 调用结果
add(1, '', null, 2, 3) // 结果:"1null23"

// dispatcher 调用结果
dispatcher(1, '', null, 2, 3).then((res) => {
  console.log(res) // 结果:12
})

接口请求

import mixin from 'mixin-middleware'

const ajax = (params) => {/* 省略实现代码 */}

const request = mixin({ action: ajax })

// 注册实例中间件
// 处理 url 前缀的中间件
request.use(async (ctx, next) => {
  const { req } = ctx
  const { url } = req

  if (url.indexOf('/api') !== 0) {
    ctx.req.url = `/api/${url}`
  }

  // 等待下一个中间件执行完成
  await next()

  // 此处获取接口返回结果
  console.log(ctx.res)
})

// 发起请求
request({ url: '/fake-url' })
  .then((res) => {
    // 业务代码
  }).catch((error) => {
    // 处理错误
  })

函数签名

type NOOP = (...args: never[]) => unknown

interface MixinOptions<T = NOOP> {
  /**
   * 需要包装的函数
   */
  action: T
  /**
   * 统一错误处理
   */
  errorHandler?: (error: unknown) => void
}

杂项

洋葱中间件是面向切面(AOP)的,因此 mixin-middleware 的适用范围很广,期待你使用它做出一些非常棒的东西。