前言

其实在刚开始学习Ajax的时候,一直有一个疑问,在项目业务中有那么多请求,当遇到针对多个请求进行一些统一的的操作的时候,对每一个请求都写相同的东西,岂不是很复杂?后来知道了有请求拦截器响应拦截器,才明白原来可以统一处理。后来,自己做博客的后台管理系统的时候,需要封装请求和响应拦截器,关于如何处理响应拦截有了些思路,于是在这里分享下。(由于是没什么经验的小白,写的代码和处理思路可能存在问题,请指正👇)

需求分析

我的后台管理系统使用vue的单页应用,使用的 http的请求库是axios,关于它的用法就不说了,请自行谷歌。在项目中对于响应的处理有几点诉求:

  1. 能够对一些响应异常的状态进行统一处理
  2. 对于一些需要单独处理的异常可以在业务代码处单独处理
  3. 针对已知的状态码可以有不同的响应处理逻辑
  4. 代码调试的时候,响应数据不需要每次都手动打印到控制台
  5. ……

在业务代码中,我期望的处理形式是这样的。

// 业务代码中处理逻辑
async getTagList() {
    const res = await getTagList()
    if (res.flag) {
      // 正确的响应时的处理
      this.tags = res.data.map((item) => ({
        text: item.name,
        value: item.id
      }))
      return
    }
    // 错误时的响应处理,我只需关注业务数据的处理,一些友好提示就交给统一处理的代码
    this.tags = []
}
//单独处理异常,当我需要对内部异常码进行单独处理的时候,可以访问到原始的响应数据
const res = await this.login(data)
if (res.rootResponse.code === 2004) {
    this.usernameError.flag = !res.flag
    this.usernameError.msg = res.rootResponse.msg
    return
}
if (res.rootResponse.code === 2007) {
    this.passwordError.flag = !res.flag
    this.passwordError.msg = res.rootResponse.msg
    return
}
复制代码

代码实现

  • 响应拦截器处的代码
// 响应拦截器
http.interceptors.response.use(
    // 200的响应
  (response) => {
    const result = handleResponse(response)
    // 打印响应控制台
    if (env === 'dev') {
      console.log(result)
    } 
    return result
  },
  // 非200的响应
  (error) => {
    return handleResponse(error.response)
  }
)
复制代码
  • 响应处理函数的封装
// 引入对各个状态码的处理函数
import { handle200, handle401, handle404, handle500 } from './handler'
/** * @param {String} flag 默认标志位 * @param {Function} callback 回调函数 * @return {Function} 处理函数 * 工厂函数,制造处理函数 */
const handler = (flag, callback) => {
  return (response) => {
    const formatRes = {
      data: null,//处理后的响应数据
      rootResponse: response,//原始的响应数据
      flag: flag //标志位,响应成功了还是失败
    }
    //利用引用类型的特征修改内部的数据
    callback(response, formatRes)
    // 返回格式化的响应数据
    return formatRes
  }
}

// 创建处理响应的处理器
const httpExceptionMap = new Map()
// 利用Map的键值可以为非字符串类型,注册响应处理函数,针对浏览器返回的状态码的处理函数
httpExceptionMap.set(200, handler(true, handle200))
httpExceptionMap.set(400, handler(false, handle500))
httpExceptionMap.set(401, handler(false, handle401))
httpExceptionMap.set(404, handler(false, handle404))
httpExceptionMap.set(500, handler(false, handle500))

// 统一处理的入口,进行状态处理的分发
const handleResponse = function(response) {
  const resStatus = response.status // 获取返回的处理的状态码
  if (httpExceptionMap.has(resStatus)) {
    // 已知错误,分发处理
    return httpExceptionMap.get(resStatus)(response.data)
  } else {
      // 未知错误,统一500,服务器错误
    console.log('发生未知响应错误 >>', response)
    return handler(false, handle500)(response.data)
  }
}
复制代码
  • 内部自定义异常的处理
import store from '@/store'
// 200
export const handle200 = function(response, formatRes) {
  if (response.code === 200) {
    // 对于正常的内部状态码,直接返回处理的数据即可
    formatRes.data = response.data
    return
  }
  // 对于异常的内部状态码,需要统一处理的进行处理,并将响应的标志位置为 false
  if (response.code === 2100) {
    store.dispatch('popup/showSnackbar', [response.msg, 'error'])
  }
  if (response.code === 2005) {
    store.dispatch('popup/showSnackbar', [response.msg, 'error'])
  }
  formatRes.flag = false
}
// 500
export const handle500 = function(response) {
  // 对于异常的响应值直接统一处理即可
  store.dispatch('popup/showSnackbar', ['服务器错误', 'error', 6000])
}
复制代码

总结

没有什么高深的逻辑,只是一个简单的封装处理的实现,但是还是存在许多不足的地方,比如,是否需要统一的的请求可以在业务代码处控制(利用请求拦截器的 config )等。等自己以后有了更好的处理方案再来接着弄。