美文网首页
5、拦截器的设计与实现

5、拦截器的设计与实现

作者: Eileen_1d88 | 来源:发表于2019-12-09 17:12 被阅读0次
需求

我们希望能对请求的发送和响应做拦截,也就是在发送请求之前和接收到响应之后做一些额外逻辑。我们希望设计的拦截器使用方式如下:

axis.interceptors.request.use(function(config) {
  // do something
  return config
}, function(error) {
  return Promise.reject(error)
})
axis.interceptors.response.use(function(response) {
  // do something
  return response
}, function(error) {
  return Promise.reject(error)
})

在axios对象上有一个interceptors对象属性,该属性又有request和response 2个属性,他们都有一个use方法,use方法支持2个参数,第一个参数类似Promise的resolve函数,第二个函数类似Promise的reject函数。我们可以在resolve函数和reject函数中执行同步代码或异步代码。
宾切我们是可以添加多个拦截器的,拦截器的执行顺序是链式依次执行的方式。对于request拦截器,后添加的拦截器会在请求前的过程中先执行;对于response拦截器,先添加的拦截器会在响应后先执行。

axios.interceptors.request.use(config => {
  config.headers.test += '1'
  return config
})
axios.interceptors.request.use(config => {
  config.headers.test += '2'
  return config
})

此外,我们也支持删除某个拦截器,如:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/})
axios.interceptors.request.eject(myInterceptor)
整体设计

以下是拦截器工作流程:



整个过程是一个链式调用的过程,并且每个拦截器都可以支持同步和异步处理,我们会想到使用Promise链的方式来实现整个调用过程。
在这个promise链的执行过程中, 请求拦截器resolve函数处理的是config对象,而响应拦截器resolve函数处理的是response对象。
那么接下来我们需要创建一个拦截器管理器,允许我们去添加、删除和遍历拦截器。

拦截器管理类实现

根据需求,axios拥有一个interceptors属性,该属性又有request和response 2个属性,它们
对外提供一个use方法来添加拦截器,我们可以把该属性看作是一个拦截器管理对象。use方法支持2个参数,第一个resolve函数,第二个是reject函数,对于resolve函数的参数,请求拦截器是AxiosRequestConfig类型的,而响应拦截器是AxiosResponse类型的;而对于reject函数的参数类型则any类型的。
根据上述分析,我们先来定义一下拦截器管理对象对外的接口。

接口定义
interface AxiosInterceptorManager<T> {
  // 这里返回一个数字,作为interceptor的ID,后面做删除的时候会用到
  use(resolved: ResolveFn<T>, rejected?: RejectedFn): number
  eject(id: number): void
}

interface ResolveFn<T> {
  (val: T): T | Promise<T>
}

interface RejectedFn {
  (error: any): any
}

interface Interceptors {
  request: InterceptorManager<AxiosRequestConfig>
  response: InterceptorManager<AxiosResponse>
}
interceptorManager实现

src/core/interceptorManager.ts

import { ResolveFn, RejectedFn } from "../types";

interface Interceptor<T> {
  resolved: ResolveFn<T>
  rejected?: RejectedFn
}
export default class InterceptorManager<T> {
  private interceptors: Array<Interceptor<T> | null>
  constructor() {
    this.interceptors = []
  }
  use(resolved: ResolveFn<T>, rejected?: RejectedFn): number {
    this.interceptors.push({
      resolved,
      rejected
    })
    return this.interceptors.length - 1
  }
  eject(id: number): void {
    if (this.interceptors[id]) {
      this.interceptors[id] = null
    }
  }
  forEach(fn: (Interceptor: Interceptor<T>) => void): void {
    this.interceptors.forEach(interceptor => {
      if (interceptor !== null) {
        fn(interceptor)
      }
    })
  }
}
interface Axios {
 interceptors: Interceptors
  request<T = any>(config: AxiosRequestConfig): AxiosPromise<T>
  get<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  head<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  delete<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  options<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
  post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
  patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
}
Axios 中增加interceptors
import { AxiosRequestConfig, AxiosPromise, Method, AxiosResponse, ResolveFn, RejectedFn, Interceptors } from "../types";
import dispatchRequest from "./dispatchRequest";
import InterceptorManager from "./interceptorManager";

interface PromiseChain {
  resolved: ResolveFn | ((config: AxiosRequestConfig) => AxiosPromise)
  rejected?: RejectedFn
}
export default class Axios {
  interceptors: Interceptors
  constructor() {
    this.interceptors = {
      request: new InterceptorManager<AxiosRequestConfig>(),
      response: new InterceptorManager<AxiosResponse>()
    }
  }
  request(url: string | AxiosRequestConfig, config?: AxiosRequestConfig): AxiosPromise {
    // 这里要定义一个新的newConfig变量,是因为AxiosPromise接收的泛型类型T是any,与config的类型不一致
    let newConfig: any
    newConfig = config || {}
    if (typeof url === 'string') {
      newConfig.url = url
    } else {
      newConfig = url
    }
    const chain: PromiseChain[] = [{
      resolved: dispatchRequest,
      rejected: undefined
    }]
    this.interceptors.request.forEach(interceptor => {
      chain.unshift(interceptor)
    })
    this.interceptors.response.forEach(interceptor => {
      chain.push(interceptor)
    })
    let promise = Promise.resolve(newConfig)
    while(chain.length) {
      const { resolved, rejected } = chain.shift()!
      promise = promise.then(resolved, rejected)
    }
    return promise
    // return dispatchRequest(config)
  }
  get(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestMethod(url, 'get', config)
  }
  delete(url: string, config?: AxiosRequestConfig): AxiosPromise {
    // const newConfig = Object.assign(config || {}, {
    //   url,
    //   method: 'delete'
    // })
    // return dispatchRequest(newConfig)
    return this._requestMethod(url, 'delete', config)
  }
  head(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestMethod(url, 'head', config)
  }
  options(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestMethod(url, 'options', config)
  }
  put(url: string, data?: any, config?: AxiosRequestConfig,): AxiosPromise {
    return this._requestMethod(url, 'put', config, data)
  }
  post(url: string, data?: any, config?: AxiosRequestConfig,): AxiosPromise {
    return this._requestMethod(url, 'post', config, data)
  }
  patch(url: string, data?: any, config?: AxiosRequestConfig,): AxiosPromise {
    return this._requestMethod(url, 'patch', config, data)
  }
  _requestMethod(url: string, method: Method, config?: AxiosRequestConfig, data?: any) {
    const newConfig = Object.assign(config || {}, {
      url,
      method,
      data: data || null
    })
    return this.request(newConfig)
  }
}

这样,axios就拥有了interceptors属性,interceptors属性拥有use和eject方法,拦截器添加之后,每次请求时,都会经过请求拦截器、请求、响应拦截器。至此,我们完成了拦截器的功能,实际工作中,我们可以利用它做一下登陆认证之类的工作。

我们目前通过ts-axios发送请求,往往会传入一堆配置,但是我们也希望ts-axios本身也会有一些默认配置,我们把用户传入的自定义配置和默认配置做一层合并。接下来我们来实现这个feature。

相关文章

  • 5、拦截器的设计与实现

    需求 我们希望能对请求的发送和响应做拦截,也就是在发送请求之前和接收到响应之后做一些额外逻辑。我们希望设计的拦截器...

  • (十二)Struts2进阶之拦截器

    1.拦截器底层实现原理 (1)AOP思想 (2)责任链模式(一种设计模式) 2.实现拦截器的三种方式 (1)实现I...

  • Mybatis拦截器介绍

    Mybatis拦截器介绍 Mybatis拦截器设计的初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去...

  • Mybatis拦截器

    一 Mybatis拦截器介绍 Mybatis拦截器设计的初衷就是为了供用户在某些时候可以实现自己的逻辑而不必...

  • Kotlin 协程实现原理

    目录 前言先从线程谈起 设计思想CPS 变换续体与续体拦截器状态机标准库 实现细节Main 调度器Default ...

  • SpringMVC拦截器

    SpringMVC拦截器 拦截器的定义 自定义的拦截器需要实现一个接口HandlerInterceptor,并实现...

  • okhttp(三)

    OKHttp3通过拦截链的设计,让请求分成5个拦截器去处理,拦截器各司其职,扩展性非常高。拦截链是从自定义的拦截器...

  • Spring15-拦截器

    定义拦截器 定义拦截器需要实现HandlerInterceptor 配置拦截器 注意:spring mvc的拦截器...

  • 声明事务-基于AOP或全注解方式实现

    声明事务 声明事务-实现方式 声明式事务管理的配置类型 5种类型:独立代理、共享代理、拦截器、tx拦截器、全注解(...

  • 01. struts2介绍

    struts2优点 与Servlet API 耦合性低。无侵入式设计 提供了拦截器,利用拦截器可以进行AOP编程,...

网友评论

      本文标题:5、拦截器的设计与实现

      本文链接:https://www.haomeiwen.com/subject/oqhegctx.html