MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue网络请求 拦截器的高级用法与错误捕获

2021-07-038.0k 阅读

一、Vue 网络请求基础与拦截器概述

在 Vue 项目开发中,网络请求是非常常见的操作,用于与后端服务器进行数据交互。通常我们会使用一些库来处理网络请求,比如 axios。Axios 是一个基于 Promise 的 HTTP 客户端,在 Vue 项目中被广泛应用。

拦截器在网络请求过程中扮演着重要的角色。它可以在请求发送之前或响应接收之后,对请求或响应进行一些统一的处理。例如,在请求发送前添加一些通用的请求头,如身份验证令牌(token);在响应接收后,对响应数据进行统一的格式处理或错误处理。

1.1 安装与引入 Axios

在 Vue 项目中使用 Axios,首先需要安装它。可以通过 npm 或 yarn 进行安装:

npm install axios --save
# 或者
yarn add axios

安装完成后,在项目中引入 Axios。一般可以在 main.js 文件中进行全局引入:

import Vue from 'vue'
import axios from 'axios'

Vue.prototype.$axios = axios

这样,在 Vue 组件中就可以通过 this.$axios 来使用 Axios 发送网络请求了。

1.2 基本网络请求示例

Axios 提供了多种发送网络请求的方法,如 getpostputdelete 等。以下是一个简单的 get 请求示例:

this.$axios.get('/api/user')
 .then(response => {
    console.log(response.data)
  })
 .catch(error => {
    console.error(error)
  })

上述代码发送了一个到 /api/userget 请求,当请求成功时,then 回调函数会被执行,response.data 包含了服务器返回的数据;当请求失败时,catch 回调函数会被执行,error 包含了错误信息。

二、Vue 网络请求拦截器的基本用法

2.1 请求拦截器

请求拦截器用于在请求发送之前对请求进行处理。可以通过 axios.interceptors.request.use 方法来添加请求拦截器。该方法接收两个参数,第一个参数是成功的回调函数,第二个参数是失败的回调函数(可选)。

以下是一个添加请求拦截器,在请求头中添加 token 的示例:

axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, error => {
  return Promise.reject(error)
})

在上述代码中,config 是请求的配置对象,通过 localStorage.getItem('token') 获取存储在本地的 token,如果存在 token,则在请求头的 Authorization 字段中添加 Bearer 加上 token 的值。最后返回 config,这样请求就会携带添加的 token 发送到服务器。如果在获取 token 或处理请求配置过程中出现错误,error 回调函数会被执行,通过 Promise.reject(error) 将错误传递下去。

2.2 响应拦截器

响应拦截器用于在响应被 thencatch 处理之前对响应进行处理。可以通过 axios.interceptors.response.use 方法来添加响应拦截器。同样,该方法接收两个参数,第一个参数是成功的回调函数,第二个参数是失败的回调函数。

以下是一个简单的响应拦截器示例,用于统一处理响应数据中的错误信息:

axios.interceptors.response.use(response => {
  if (response.data.code!== 0) {
    // 假设 code 为 0 表示成功,其他表示失败
    console.error('请求失败,错误信息:', response.data.message)
    return Promise.reject(new Error(response.data.message))
  }
  return response.data
}, error => {
  console.error('网络请求出现错误:', error)
  return Promise.reject(error)
})

在上述代码中,首先检查响应数据中的 code 字段,如果 code 不等于 0,则表示请求失败,打印错误信息并通过 Promise.reject 返回一个错误。如果 code 等于 0,则直接返回响应数据中的 data 部分。如果在处理响应过程中出现其他错误,error 回调函数会被执行,同样通过 Promise.reject(error) 将错误传递下去。

三、Vue 网络请求拦截器的高级用法

3.1 多拦截器管理

在一个大型项目中,可能会有多个模块需要对网络请求进行不同的预处理或后处理,这时候就需要使用多个拦截器。Axios 允许我们添加多个请求和响应拦截器。

例如,我们有一个模块需要在请求头中添加特定的业务标识,另一个模块需要对特定类型的响应进行特殊处理。

首先添加一个用于添加业务标识的请求拦截器:

axios.interceptors.request.use(config => {
  const businessFlag = 'your_business_flag'
  config.headers['Business - Flag'] = businessFlag
  return config
}, error => {
  return Promise.reject(error)
})

然后添加一个对特定响应状态码进行特殊处理的响应拦截器:

axios.interceptors.response.use(response => {
  if (response.status === 401) {
    // 处理未授权情况,例如跳转到登录页面
    router.push('/login')
    return Promise.reject(new Error('未授权,请重新登录'))
  }
  return response
}, error => {
  return Promise.reject(error)
})

通过这种方式,可以根据项目的不同需求,灵活地添加和管理多个拦截器。

3.2 条件拦截

有时候,我们可能只希望在特定条件下触发拦截器。比如,某些请求不需要添加 token,或者某些请求的响应需要特殊处理。

以下是一个条件请求拦截器的示例,只有当请求的 URL 包含特定字符串时才添加 token

axios.interceptors.request.use(config => {
  if (config.url.includes('/api/protected')) {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
  }
  return config
}, error => {
  return Promise.reject(error)
})

在上述代码中,通过 config.url.includes('/api/protected') 判断请求的 URL 是否包含 /api/protected,如果包含,则添加 token 到请求头。

对于响应拦截器,也可以实现条件拦截。例如,只对特定接口的响应进行特殊处理:

axios.interceptors.response.use(response => {
  if (response.config.url.includes('/api/special - response')) {
    // 对特定接口的响应进行特殊处理
    const specialData = response.data.specialData
    // 处理 specialData
    return specialData
  }
  return response.data
}, error => {
  return Promise.reject(error)
})

这样,就可以根据请求或响应的具体条件,灵活地决定是否执行拦截器中的逻辑。

3.3 拦截器的移除

在某些情况下,可能需要动态地移除拦截器。Axios 提供了移除拦截器的方法。每个拦截器在添加时会返回一个 id,我们可以使用这个 id 来移除拦截器。

以下是一个移除请求拦截器的示例:

// 添加请求拦截器
const requestInterceptorId = axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, error => {
  return Promise.reject(error)
})

// 移除请求拦截器
axios.interceptors.request.eject(requestInterceptorId)

同样,对于响应拦截器也可以使用类似的方法移除:

// 添加响应拦截器
const responseInterceptorId = axios.interceptors.response.use(response => {
  if (response.data.code!== 0) {
    console.error('请求失败,错误信息:', response.data.message)
    return Promise.reject(new Error(response.data.message))
  }
  return response.data
}, error => {
  console.error('网络请求出现错误:', error)
  return Promise.reject(error)
})

// 移除响应拦截器
axios.interceptors.response.eject(responseInterceptorId)

通过移除拦截器,可以在运行时动态地控制网络请求的处理逻辑,满足一些特殊的业务需求。

3.4 全局拦截器与局部拦截器

在 Vue 项目中,我们通常在 main.js 中添加的拦截器是全局拦截器,会对所有的网络请求生效。但有时候,我们可能只希望某些组件内的请求使用特定的拦截器,这就需要局部拦截器。

我们可以通过创建一个新的 Axios 实例来实现局部拦截器。例如:

import axios from 'axios'

// 创建一个新的 Axios 实例
const localAxios = axios.create({
  baseURL: '/api/local'
})

// 为局部 Axios 实例添加请求拦截器
localAxios.interceptors.request.use(config => {
  const localToken = sessionStorage.getItem('localToken')
  if (localToken) {
    config.headers.Authorization = `Bearer ${localToken}`
  }
  return config
}, error => {
  return Promise.reject(error)
})

// 在组件中使用局部 Axios 实例
export default {
  data() {
    return {}
  },
  methods: {
    async localRequest() {
      try {
        const response = await localAxios.get('/user')
        console.log(response.data)
      } catch (error) {
        console.error(error)
      }
    }
  }
}

在上述代码中,我们创建了一个新的 Axios 实例 localAxios,并为其添加了一个请求拦截器。这个拦截器只会对通过 localAxios 发送的请求生效,实现了局部拦截的功能。

四、Vue 网络请求中的错误捕获

4.1 错误类型

在 Vue 网络请求过程中,可能会遇到多种类型的错误。常见的错误类型包括:

  • 网络错误:如网络连接失败、超时等。这种错误通常是由于网络环境不稳定或服务器端问题导致的。在 Axios 中,当出现网络错误时,catch 回调函数会被执行,error.message 会包含一些关于网络错误的信息,如 Network Errortimeout of xxx ms exceeded
  • HTTP 状态码错误:当服务器返回的 HTTP 状态码不是 2xx 时,会被认为是 HTTP 状态码错误。例如,404 表示资源未找到,401 表示未授权,500 表示服务器内部错误等。Axios 默认会将非 2xx 的状态码视为错误,并将错误传递给 catch 回调函数。error.response 会包含服务器返回的响应信息,包括状态码、响应头和响应体等。
  • 业务逻辑错误:这是由服务器返回的业务数据中标识的错误,例如业务逻辑校验失败,返回的 data 中包含错误码和错误信息。在前面的响应拦截器示例中,我们通过检查 response.data.code 来判断业务逻辑是否成功,如果失败则抛出错误。

4.2 全局错误捕获

通过响应拦截器可以实现全局的错误捕获和处理。在前面的响应拦截器示例中,我们已经展示了如何对业务逻辑错误和 HTTP 状态码错误进行处理。对于网络错误,Axios 也会将其传递到响应拦截器的 error 回调函数中。

以下是一个更完善的全局错误捕获的响应拦截器示例:

axios.interceptors.response.use(response => {
  if (response.data.code!== 0) {
    // 业务逻辑错误处理
    console.error('业务逻辑错误,错误信息:', response.data.message)
    return Promise.reject(new Error(response.data.message))
  }
  return response.data
}, error => {
  if (error.response) {
    // HTTP 状态码错误处理
    console.error('HTTP 状态码错误,状态码:', error.response.status)
    console.error('错误信息:', error.response.data)
  } else if (error.request) {
    // 网络错误处理,error.request 是一个 XMLHttpRequest 实例
    console.error('网络错误,请求未收到响应')
  } else {
    // 其他错误处理
    console.error('请求发生错误:', error.message)
  }
  return Promise.reject(error)
})

在上述代码中,对于不同类型的错误进行了分类处理。如果 error.response 存在,说明是 HTTP 状态码错误,打印状态码和响应数据;如果 error.request 存在,说明是网络错误,请求未收到响应;否则,打印其他类型的错误信息。

4.3 局部错误捕获

在组件内部发送网络请求时,也可以进行局部的错误捕获。通过在 catch 块中处理错误,可以针对组件特定的需求进行错误处理。

以下是一个组件内局部错误捕获的示例:

export default {
  data() {
    return {}
  },
  methods: {
    async fetchData() {
      try {
        const response = await this.$axios.get('/api/data')
        console.log(response.data)
      } catch (error) {
        // 局部错误处理
        if (error.response && error.response.status === 404) {
          console.error('组件内:资源未找到')
        } else {
          console.error('组件内:其他错误', error)
        }
      }
    }
  }
}

在上述代码中,fetchData 方法通过 try...catch 捕获网络请求过程中的错误。如果 error.response 存在且状态码为 404,则打印 组件内:资源未找到;否则,打印其他类型的错误信息。这样可以在组件级别对特定的错误进行针对性处理,而不影响全局的错误处理逻辑。

五、错误处理的最佳实践

5.1 统一错误日志记录

在项目开发中,统一的错误日志记录非常重要。无论是全局错误还是局部错误,都应该记录下来,以便于排查问题。可以使用一些日志记录工具,如 console(在开发环境)或专业的日志服务(在生产环境)。

在全局响应拦截器中,可以添加更详细的日志记录逻辑:

import Logger from 'your - logger - library'

axios.interceptors.response.use(response => {
  if (response.data.code!== 0) {
    const errorMessage = `业务逻辑错误,错误信息:${response.data.message}`
    Logger.error(errorMessage)
    return Promise.reject(new Error(response.data.message))
  }
  return response.data
}, error => {
  let errorMessage = '请求发生未知错误'
  if (error.response) {
    errorMessage = `HTTP 状态码错误,状态码:${error.response.status},错误信息:${error.response.data}`
  } else if (error.request) {
    errorMessage = '网络错误,请求未收到响应'
  } else {
    errorMessage = `请求发生错误:${error.message}`
  }
  Logger.error(errorMessage)
  return Promise.reject(error)
})

在上述代码中,使用了一个假设的日志记录库 Logger,将不同类型的错误信息记录到日志中。这样在生产环境中,可以通过查看日志快速定位问题。

5.2 用户友好的错误提示

对于用户来说,看到的错误提示应该是友好和易懂的。在全局或局部错误处理中,应该根据错误类型向用户展示合适的提示信息。

例如,在全局响应拦截器中,可以使用 Vue 的 message 组件(如 Element - UI 的 Message)来展示错误提示:

import { Message } from 'element - ui'

axios.interceptors.response.use(response => {
  if (response.data.code!== 0) {
    Message.error('业务逻辑错误,请稍后重试')
    return Promise.reject(new Error(response.data.message))
  }
  return response.data
}, error => {
  if (error.response && error.response.status === 404) {
    Message.error('资源未找到')
  } else if (error.response && error.response.status === 401) {
    Message.error('未授权,请重新登录')
  } else if (error.request) {
    Message.error('网络连接异常,请检查网络')
  } else {
    Message.error('请求发生错误,请稍后重试')
  }
  return Promise.reject(error)
})

通过这种方式,用户可以直观地了解到错误的大致原因,提高用户体验。

5.3 错误重试机制

在某些情况下,网络错误可能是由于临时的网络波动或服务器繁忙导致的。为了提高请求的成功率,可以实现错误重试机制。

以下是一个简单的错误重试示例,使用 async/await 和递归实现:

async function retryRequest(requestFunction, maxRetries = 3, delay = 1000) {
  let retries = 0
  while (retries < maxRetries) {
    try {
      return await requestFunction()
    } catch (error) {
      retries++
      if (retries === maxRetries) {
        throw error
      }
      await new Promise(resolve => setTimeout(resolve, delay))
    }
  }
}

// 使用示例
async function fetchData() {
  const requestFunction = () => this.$axios.get('/api/data')
  try {
    const response = await retryRequest(requestFunction)
    console.log(response.data)
  } catch (error) {
    console.error('最终请求失败:', error)
  }
}

在上述代码中,retryRequest 函数接收一个请求函数 requestFunction、最大重试次数 maxRetries 和重试间隔时间 delay。在 while 循环中尝试执行请求函数,如果请求失败且重试次数未达到最大次数,则等待 delay 时间后再次尝试。如果达到最大重试次数仍失败,则抛出错误。通过这种方式,可以在一定程度上提高网络请求的稳定性。

六、总结与扩展

通过对 Vue 网络请求拦截器的高级用法和错误捕获的深入探讨,我们了解到拦截器不仅可以实现请求和响应的统一处理,还可以通过多拦截器管理、条件拦截、拦截器移除以及全局与局部拦截等方式,满足复杂项目的各种需求。在错误捕获方面,了解不同类型的错误,并通过全局和局部错误捕获机制,结合统一错误日志记录、用户友好的错误提示以及错误重试机制等最佳实践,可以提高项目的稳定性和用户体验。

在实际项目开发中,还可以进一步扩展这些功能。例如,结合 Vuex 来管理网络请求的状态,在请求开始时设置 loading 状态,请求成功或失败时清除 loading 状态,从而实现更好的用户交互。另外,对于一些复杂的业务场景,可能需要对请求进行排队处理,以避免同时发起过多请求导致性能问题。总之,合理运用网络请求拦截器和错误捕获机制,可以让 Vue 项目在数据交互方面更加健壮和高效。