当前位置: 首页 > article >正文

axios的替代方案onion-middleware

onion-middleware的由来

嗯。。。闲来无事瞎搞的!!!!主要用来实现请求/相应拦截,当然队列性的数据操作都是可以的

直接上使用教程

  1. 安装
npm install onion-middleware
  1. 使用
import { OnionMiddleware } from 'onion-middleware'

// vite等模块化加载的工具需调用clearMiddleware
app.clearMiddleware()

// 创建一个中间件应用
const app = new OnionMiddleware()

// 添加中间件
app.use(async (ctx, next) => {
  // Middleware Before to do something
   next()
  // Middleware After to do something
})

  1. 实战使用
import { BaseResponse, CtxType, FetchOptionsType } from './type';
import storage from '../storage';
import { sortObject } from '../common';
import { aesDecrypt, aesEncrypt } from './cryproUtil';
import config from '@/configs/config';
import { message } from 'antd';
import { OnionMiddleware } from 'onion-middleware'
const pendingPromises: { [key: string]: any } = {};
export const fetchApi = (options: FetchOptionsType) => {
  const { url, timeout, headers, method, data = {}, ...args } = options || {};
  const queryData =
    data instanceof FormData ? data : JSON.parse(JSON.stringify(data)); // 去掉undefined的属性值
  let conpleteUrl = url;
  const reqOptions = { ...args, headers, method };

  if (['GET', 'DELETE'].includes(method?.toUpperCase())) {
    conpleteUrl = url + '?';
    for (let key in queryData) {
      const val = queryData[key]
      conpleteUrl += `${key}=${typeof val === 'object' ? JSON.stringify(val) : encodeURIComponent(queryData[key])}&`;
    }
  } else {
    reqOptions.body =
      typeof queryData === 'string' || queryData instanceof FormData
        ? queryData
        : JSON.stringify(queryData);
    if (queryData instanceof FormData) {
      delete reqOptions.headers['Content-Type'];
    }
  }

  return Promise.race([
    fetch(conpleteUrl, reqOptions)
      .then((response) => {
        return response.json();
      })
      .catch((e) => {
        return JSON.stringify({
          code: 504,
          msg: '连接不到服务器',
        });
      }),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Request timeout')), timeout)
    ),
  ]);
};

const app = new OnionMiddleware()
app.clearMiddleware();

// 特殊业务处理中间件(401),以及相应数据的ts类型定义
app.use(async (ctx: CtxType, next: (arg?: any) => void) => {
  // Middleware Before
  await next();
  // Middleware After
  const { response } = ctx;
  // 401,1401 重新静默登录
  if ([401, 1401].includes(response?.code)) {
    // 未登录
    storage.clearAll();
    message.error('登录过期,请重新登陆!');
    // to do something
  }
});

// 重复请求复用中间件
app.use(async (ctx: CtxType, next: (arg?: any) => void) => {
  // Middleware Before
  const { request } = ctx;
  // 使用请求信息作为唯一的请求key,缓存正在请求的promise对象
  // 相同key的请求将复用promise
  const requestKey = JSON.stringify([
    request.method.toUpperCase(),
    sortObject(request.data),
  ]);
  if (pendingPromises[requestKey]) {
    console.log('重复请求,已合并请求并返回共享的第一次请求,参数:', request);
    await next({
      shouldAbort: true,
      callback: () => {
        return pendingPromises[requestKey];
      },
    });
  } else {
    await next();
  }
  // Middleware After
  delete pendingPromises[requestKey];
});

// 请求/相应信息输出中间件
app.use(async (ctx: CtxType, next: (arg?: any) => void) => {
  // Middleware Before
  await next();
  // Middleware After
  const { request, response } = ctx;
  if (request?.errorTip && ![200, 401, 1401].includes(response.code)) {
    message.error(response.msg);
  }
  console.log('请求参数', {
    url: request?.url,
    data: request?.originData,
    method: request?.method,
  });
  console.log('响应参数', response);
});

// header处理中间件(此中间件位于)
app.use(async (ctx: CtxType, next: (arg?: any) => void) => {
  // Middleware Before
  const { request, response } = ctx;
  const token = storage.getItem('token');
  if (token) {
    // 请求头token信息,请根据实际情况进行修改
    request.headers['Authorization'] = 'Bearer ' + token;
  }
  ctx.request = request;

  await next();

  // Middleware After
});

// 加解密
app.use(async (ctx: CtxType, next: (arg?: any) => void) => {
  // Middleware Before
  const { request } = ctx;
  request.originData = request.data;
  if (config.OPEN_ENCRYPTION) {
    const aesData = aesEncrypt(request.data);
    // 加密
    request.data = { encryptedReqData: aesData[0] };
    // sign
    request.headers['sign'] = aesData[1];
  }
  await next();
  // Middleware After
  if (config.OPEN_ENCRYPTION) {
    if (ctx.response?.encryptedResData) {
      ctx.response = aesDecrypt(ctx.response?.encryptedResData || '') || '{}';
    }
  }
});

// 公共请求发送方法(简约)
const send = <T>(options: any): Promise<T> => {
  const { errorTip = true } = options;
  // HTTP请求上下文
  const ctx: CtxType = {
    request: {
      ...options,
      errorTip,
      config: {
        timeout: 60 * 1000,
        baseUrl: config.VITE_API_URL + config.VITE_BASE_URL,
      },
      headers: {
        'Content-Type': 'application/json',
      },
    },
    promise: { resolve: () => { }, reject: () => { } },
  };

  const baseRequest = new Promise((resolve: (arg: T) => void, reject) => {
    // 打破promise回调作用域,在其他地方实现调用
    ctx.promise.resolve = resolve;
    ctx.promise.reject = reject;

    // 执行中间件
    app
      .execute(ctx, () => {
        let { config } = ctx?.request || {};
        const { data, method, url, headers } = ctx?.request || {};
        const fetchPromise = new Promise(async (_resolve) => {
          config = {
            ...config,
            data,
            headers,
            url: config.baseUrl + (url || ''),
            method: method.toUpperCase(), // 配置method方法
          };
          const res = await fetchApi({
            ...options,
            ...config,
          });
          _resolve(res);
        });

        return fetchPromise;
      })
      .then(() => { })
      .catch((err) => {
        console.log(err);
      });
  });
  // 使用请求信息作为唯一的请求key,缓存正在请求的promise对象
  // 相同key的请求将复用promise
  const requestKey = JSON.stringify([
    options.method.toUpperCase(),
    options.url,
    sortObject(options.data),
  ]);
  // 存储第一次请求引用 (重复请求判断需要)
  if (!pendingPromises[requestKey]) {
    pendingPromises[requestKey] = baseRequest;
  }
  return baseRequest;
};

// 公共请求发送方法
const sendApi = <T = BaseResponse>(options: FetchOptionsType): Promise<T> => {
  return send(options);
};

export default sendApi;

效果简单截个图吧,拿请求/相应信息输出中间件为例,效果如下:

在这里插入图片描述

结尾

轻点喷😂😂😂


http://www.kler.cn/a/499841.html

相关文章:

  • 基于单片机的指纹密码锁
  • DSP+Simulink——点亮LED灯(TMSDSP28379D)超详细
  • Windows 下安装 PyTorch 的常见问题及解决方法
  • 在php中,Fiber、Swoole、Swow这3个协程都是如何并行运行的?
  • 【HTML+CSS+JS+VUE】web前端教程-3-标题标签
  • Seata搭建
  • 解锁 C# 与 LiteDB 嵌入式 NoSQL 数据库
  • JavaScript的输出
  • 浅谈容灾技术方案详解
  • 2025年实训总结
  • 大语言模型的前沿探索:从理论到实践的深度剖析
  • 深入理解计算机系统——优化程序性能(一)
  • 类加载器和双亲委派
  • 深度学习与机器学习的关系和差别?
  • CMD批处理命令入门(4)——ping,ipconfig,arp,start,shutdown,taskkill
  • 【Unity3D】利用IJob、Burst优化处理切割物体
  • Redis 多路复用(Multiplexing)
  • git相关操作笔记
  • LLM的实验平台有哪些:快速搭建测试大语言模型
  • 【Unity-和WPF结合的优势】
  • Pixel 6a手机提示无法连接移动网络,打电话失败!
  • 太原理工大学软件设计与体系结构 --javaEE