项目中经常需要这样的功能:对每个http请求的请求参数和响应数据进行拦截,比如统一在headers中添加 authorization的权限验证、在响应之后统一处理返回的错误:401提示用户"您的登录信息已过期,请重新登录"。想想如果我们要在每个接口中都分别做处理,这将是多么可怕的工作量。

所以我们需要用到拦截器,顾名思义:拦截每一次你的请求和响应,然后进行相应的处理。当然这不用拦截器你或许也可以完成上述这功能,但是会很麻烦而且代码会产生大量重复。

笔者一直使用TypeScript从事Angular项目开发,Angular 给应用提供了一个简化的 HTTP 客户端 API,也就是 @angular/common/http 中的 HttpClient 服务类,使用 HttpClient 与后端服务进行通信,可以很方便的定义一个拦截器HttpInterceptor进行拦截请求和响应。

注意:只有使用 HttpClient 与后端服务进行通信,才可以使用Angular封装好的HttpInterceptor拦截器

但是有一个项目中使用的是axios封装的请求,需要使用拦截的时候笔者还懵了一下下,不能使用HttpInterceptor拦截了!后来发现 axios 有自己拦截器。

一、在http请求service(ConnectionService)中引入axios
// connection.service.ts
import axios from 'axios'  //引入axios
二、创建一个axios实例
private instance = axios.create({
  baseURL: ConstantService.HOST,
  withCredentials: true,
  headers: {},
  validateStatus: function (status) {
    return status >= 200 && status < 500 // default
  },
})
三、编写请求拦截器

此拦截器会在你发送请求之前运行

笔者此项目中请求拦截器的功能是每一次请求时去判断本地存储的一个变量iframe是否为true,如果iframe=true则把请求头的url添加“thirdParty”改变路径,并添加一些额外的鉴权参数,供后台验证。

// http request 拦截器
this.instance.interceptors.request.use(
  config => {
    const authParams: any = LocalStorage.get(STORAGE_KEY.IFRAME_PARAMS)
    const {iframe, ...rest} = authParams
    if (iframe) {
      // 替换url,替换参数
      const {url, baseURL, params} = config
      let [pathname, search] = url.split('?')
      if (pathname) {       // 替换参数
        pathname = pathname.replace(ConstantService.version, `${ConstantService.version}/thirdParty`)
      }
      const temp = search ? this.parseParams(search) : {}
      Object.assign(temp, rest)          // 覆盖原请求中的部分参数
      const newParams = this.getQueryString(temp)
      config.url = newParams ? `${pathname}?${newParams}` : pathname        // 使用?拼接成新的url
    }
    Debug.log('config.url', config.url)
    return config
  },
  err => {
    return Promise.reject(err)
  })
  
/**
   * @desc 解析跳转进来的路由参数,为对象
   * '?type=1&iframe=true'
   * =>
   * {type: "1"
      iframe: "true"
      }
   */
  parseParams(search) {
    const ret = {},
     seg = search.replace(/^\?/, '').split('&'),
     len = seg.length
    let i = 0, s
    for (; i < len; i++) {
      if (!seg[i]) {
        continue
      }
      s = seg[i].split('=')
      ret[s[0]] = s[1]
    }
    return ret
  }
四、响应拦截器
// http response 拦截器
this.instance.interceptors.response.use(function (response) {
  // 对返回的数据进行一些处理
  if ([401, 403].includes(response.status)) {
    return Promise.reject(response)
  }
  return response
}, function (error) {
  // 对返回的错误进行一些处理
  return Promise.reject(error)
})

最后在module中注入ConnectionService

import {NgModule} from '@angular/core'
import {CommonModule} from '@angular/common'
import {ConnectionService} from './services/connection.service'

@NgModule({
  ...
  providers: [
    ConnectionService,
    ...
  ],
})

export class AppModule {
}

在需要的页面导入就可以使用了,axios的用法不再赘述。