前端请求拦截器:从原理到实现,flyio 的应用实践和优化技巧

lxf2023-04-20 20:02:02

前言

前端项目中,请求频率过高、重复提交等问题是非常常见的。这些问题不仅会影响用户的使用体验,还会给后端服务器带来额外的计算和存储压力。为了解决这些问题,我们可以使用前端请求拦截器进行限流和拦截,从而优化前端请求的效率和用户体验。

在前端项目中,常用的请求库有许多,例如 Axios、Fetch、Request 等等,但在本文中,我们将介绍 flyio 请求库,并演示如何使用 flyio 请求拦截器进行限流和拦截。我们将详细讲解 flyio 请求拦截器的原理、应用和优化技巧,帮助读者了解前端请求拦截器的基本概念和实现方式,提升前端项目的性能和用户体验。

本文将对前端请求拦截器进行深入探究,详细介绍 flyio 请求拦截器的原理和实现方式。同时,本文还会给出 flyio 请求拦截器的应用实践和优化技巧,为读者提供全面的学习资料和实践指导。

希望本文能够对读者在前端项目开发中解决请求频率过高、重复提交等问题提供有益的帮助。

拦截器的原理

在 flyio 请求库中,请求拦截器是一个非常重要的概念。它可以帮助我们对请求进行限流、拦截和重试,从而保证请求的效率和稳定性。

请求拦截器是一个函数,它可以在请求发送之前或者之后对请求进行一些额外的处理。在 flyio 请求库中,请求拦截器由 interceptors 对象进行管理。其中,interceptors.requestinterceptors.response 对象分别表示请求拦截器和响应拦截器。

在请求拦截器中,我们可以对请求进行如下处理:

  • 修改请求参数:比如添加请求头、修改请求方法等;
  • 对请求进行拦截:比如判断是否需要进行请求限流、防止表单重复提交等;
  • 对请求进行重试:比如在网络异常等情况下,自动重试请求;
  • 对响应进行拦截:比如判断响应状态码,根据状态码进行错误处理等。

下面,我们通过具体的代码实例,讲解 flyio 请求拦截器的具体实现原理

请求拦截器的实现

首先,我们需要创建一个 Fly 实例,并在该实例中添加请求拦截器。

import Fly from 'flyio'

const fly = new Fly()

// 添加请求拦截器
fly.interceptors.request.use((config) => {
  // 在这里对请求进行拦截和处理
  return config
})

在上述代码中,我们使用 fly.interceptors.request.use 函数添加了一个请求拦截器。该函数接收一个函数作为参数,该函数在发送请求之前被调用。在该函数中,我们可以对请求进行拦截和处理,并返回处理后的请求参数。

例如,下面的代码中,我们对请求参数进行了一些修改,添加了一个自定义的请求头,并在请求参数中添加了一些额外的参数:

// 添加请求拦截器
fly.interceptors.request.use((config) => {
  // 添加自定义请求头
  config.headers['X-Requested-With'] = 'XMLHttpRequest'

  // 在请求参数中添加额外参数
  config.params['timestamp'] = Date.now()
  // JWT
  config.params['token'] = Date.now()
  return config
})

拦截器的应用实践

除了在请求拦截器中对请求进行修改外,我们还可以在请求拦截器中对请求进行拦截。例如,下面的代码中,我们判断了当前是否有请求正在进行中,如果有,则阻止当前请求的发送:

// 添加请求拦截器
fly.interceptors.request.use((config) => {
  // 如果当前已经有请求正在进行中,则阻止当前请求的发送
  if (window.isRequesting) {
    return Promise.reject(new Error('当前请求正在进行中,请稍后再试!'))
  }

  // 设置标识

我看过在很多人开发前端项目中,其实没有创建实例的过程,其实修改的事默认全局的flyio,这样的情况下,在请求不同的域名接口下,很容易造成请求头污染问题, 一般情况下,还是建议大家新建一个实例

前端请求拦截器:从原理到实现,flyio 的应用实践和优化技巧

在上述代码中,我们判断了 window.isRequesting 是否为 true,如果是,则直接返回一个 Promise,并抛出一个错误。这样,当前的请求就会被拦截并取消发送。

除了在请求拦截器中进行拦截和处理外,我们还可以在请求拦截器中对请求进行重试。例如,下面的代码中,我们定义了一个 maxRetry 变量,表示最大的重试次数。如果请求发送失败,则自动重试请求,最多重试 maxRetry 次:

// 添加请求拦截器
fly.interceptors.request.use((config) => {
  // 在请求参数中添加重试次数
  config.retryCount = config.retryCount || 0

  return config
})

// 添加响应拦截器
fly.interceptors.response.use(
  (response) => {
    return response.data
  },
  (err) => {
    const config = err.request
    const { retryCount = 0 } = config

    // 判断是否需要重试
    if (retryCount < maxRetry) {
      config.retryCount += 1

      // 延时重试
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(fly.request(config))
        }, retryDelay)
      })
    } else {
      return Promise.reject(err)
    }
  }
)

在上述代码中,我们使用 config.retryCount 变量记录了当前请求已经重试的次数。在响应拦截器中,如果请求发送失败,则判断当前已经重试的次数是否小于最大重试次数。如果小于最大重试次数,则将 config.retryCount 加一,并延时一定的时间后重试请求。如果已经重试了最大次数,则直接抛出错误。

拦截器的优化技巧

在实际的业务场景中 ,我们经常会出现发送重复请求的问题,这有可能是函数副作用,多次用户点击,生命周期多次执行等问题导致的,那我们能否在请求这个层面上直接把它处理拦截呢?

拦截指定时间内重复发起的请求
const throttle = (fn, time) => {
  let timer = null;
  let lastArgs = null;

  return (...args) => {
    if (!timer) {
      fn(...args);
      lastArgs = args;
      timer = setTimeout(() => {
        if (lastArgs) {
          fn(...lastArgs);
          lastArgs = null;
        }
        timer = null;
      }, time);
    } else {
      lastArgs = args;
    }
  };
};

该函数接受两个参数:

  • fn: 需要拦截的函数,这里指的是发送请求的函数。
  • time: 指定的时间间隔,单位为毫秒。在这个时间间隔内,重复的请求将被拦截。
创建一个 flyio 请求连接器,并将上一步中的函数封装进去。
const request = fly.config.baseURL;

const flyioInstance = new fly();

flyioInstance.config.baseURL = request;

flyioInstance.interceptors.request.use((config, promise) => {
  config.cancelToken = new fly.CancelToken();

  const throttleFn = throttle((config) => {
    promise.resolve(config);
  }, 1000);

  throttleFn(config);
});

需要注意的是,在上面的代码中,我们使用了 fly.CancelToken 来取消请求。如果您需要在拦截器中进行请求的取消,您可以将其设置为请求配置cancelToken 属性。

以上就是一个基于 flyio 实现 RESTful 风格接口,拦截指定时间内重复发起的请求的方案。需要注意的是,该方案只适用于 GET 请求,并且只能拦截重复的完全相同的请求。如果需要拦截更复杂的情况,您可以根据具体需求进行调整。

优化调整

在我看来,优化调整的方向取决于我们的代码风格 和业务方向,比如我个人喜欢链式调用,如代码所示

// 使用 fly.get 方法发送请求
fly.get('https://example.com/api/data')
  .then((data) => {
    console.log(data)
  })
  .catch((err) => {
    console.error(err)
  })

// 使用 fly.post 方法发送请求
fly.post('https://example.com/api/data', { name: '张三', age: 18 })
  .then((data) => {
    console.log(data)
  })
  .catch((err) => {
    console.error(err)
  })

我不想用新的request方法才能取消重复请求,那么以上方法就需要继续去优化

方案一:利用缓存,限制重复URL的请求

在上面的代码中,我们通过一个对象 requestingUrls 存储正在进行中的请求的 URL,然后在请求拦截器中判断当前 URL 是否已经在 requestingUrls 中,如果是,则说明当前请求正在进行中,需要拦截并返回一个错误;如果不是,则将当前 URL 标记为正在请求中。在响应拦截器中,我们在请求完成后将当前 URL 的标记从 requestingUrls 中删除。

// 存储正在进行中的请求
const requestingUrls = {}

// 添加请求拦截器
fly.interceptors.request.use((config) => {
  // 获取请求的 URL
  const url = config.url

  // 判断当前 URL 是否正在请求中
  if (requestingUrls[url]) {
    return Promise.reject(new Error('请勿重复提交请求'))
  }

  // 标记当前 URL 正在请求中
  requestingUrls[url] = true

  // 指定请求超时时间
  config.timeout = requestTimeout

  return config
})

// 添加响应拦截器
fly.interceptors.response.use(
  (response) => {
    // 取消当前 URL 的请求标记
    delete requestingUrls[response.config.url]

    return response.data
  },
  (err) => {
    // 取消当前 URL 的请求标记
    delete requestingUrls[err.config.url]

    return Promise.reject(err)
  }
)

方案二:限制重复URL的请求,指定时间段

接着我们来完善以上方案。首先需要引入Flyio和lodash的throttle函数

FlyWithInterceptor类中定义了添加和移除请求拦截器的方法,以及get和post请求方法。

在createThrottledInterceptor方法中,我们使用了throttle函数来创建一个节流函数,用于限制同一个URL在指定时间段内只能发送一次请求。

import Fly from 'flyio';
import throttle from 'lodash/throttle';

class FlyWithInterceptor {
  private fly: Fly;
  private interceptors: {
    request: Fly.FlyInterceptor[],
    response: Fly.FlyInterceptor[]
  };

  constructor() {
    this.fly = new Fly();
    this.interceptors = {
      request: [],
      response: []
    };
  }

  public addRequestInterceptor(onFulfilled?: Fly.FulfilledFunc<any>, onRejected?: Fly.RejectedFunc): number {
    return this.interceptors.request.push(this.fly.interceptors.request.use(onFulfilled, onRejected));
  }

  public addResponseInterceptor(onFulfilled?: Fly.FulfilledFunc<any>, onRejected?: Fly.RejectedFunc): number {
    return this.interceptors.response.push(this.fly.interceptors.response.use(onFulfilled, onRejected));
  }

  public removeRequestInterceptor(interceptorId: number): void {
    this.fly.interceptors.request.eject(this.interceptors.request[interceptorId - 1]);
  }

  public removeResponseInterceptor(interceptorId: number): void {
    this.fly.interceptors.response.eject(this.interceptors.response[interceptorId - 1]);
  }

  public get<T>(url: string, params?: Fly.FlyRequestConfig<T>): Promise<T> {
    return this.fly.get(url, params);
  }

  public post<T>(url: string, params?: Fly.FlyRequestConfig<T>): Promise<T> {
    return this.fly.post(url, params);
  }

  public createThrottledInterceptor(time: number): Fly.FlyInterceptor {
    const throttled = throttle((config: Fly.FlyRequestConfig<any>, cancel: Fly.CancelTokenSource) => {
      const url = config.url;
      if (throttled.lastCall && throttled.lastCall[0] === url) {
        cancel('Duplicate request');
      } else {
        throttled.lastCall = [url];
      }
    }, time);

    return (config: Fly.FlyRequestConfig<any>, cancel: Fly.CancelTokenSource) => {
      throttled(config, cancel);
    };
  }
}

接下来我们来使用这个FlyWithInterceptor类

const flyInstance = new FlyWithInterceptor();

flyInstance.addRequestInterceptor((config) => {
  config.baseURL = 'https://api.example.com';
  return config;
});

flyInstance.addResponseInterceptor((response) => {
  if (response.status !== 200) {
    return Promise.reject(response);
  }

  return response.data;
});

const throttledInterceptor = flyInstance.createThrottledInterceptor(5000);

flyInstance.addRequestInterceptor(throttledInterceptor);

flyInstance.get('/data').then((data) => {
  console.log(data);
}).catch((error) => {
  console.error(error);
});

flyInstance.post('/data', { name: 'fly', version: '0.0.1' }).then((data) => {
  console.log(data);
}).catch((error) => {
  console.error(error);
});

总结

通过上述实例,我们可以看到 flyio 请求拦截器的具体实现原理。

请求拦截器是一个非常实用的功能,可以帮助我们对请求进行限流、拦截和重试,从而保证请求的效率和稳定性。

在实际的开发中,我们可以根据实际需求,使用请求拦截器来对请求进行个性化的处理和定制。