在现代前端开发中,我们经常需要向服务器发送 HTTP 请求,并根据响应内容做不同的处理。axios
是一个流行的 HTTP 库,提供了 拦截器
功能,可以在请求和响应阶段插入自定义逻辑,这使得我们在处理认证、错误提示等场景时更为简洁、统一。
本文将讲解如何利用 axios
的请求拦截器和响应拦截器来处理 token、重定向、错误提示等场景,并附上一些常见的面试问题。
一、代码实现
首先,我们来看一个使用 axios
的拦截器实例代码:
import axios, { type AxiosResponse } from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'
import { ResultEnum } from '@/enums/ResultEnum'
import { TOKEN_KEY } from '@/enums/SystemEnum'
// 创建 axios 实例
const AXIOS = axios.create({
baseURL: '/lili-api',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// 请求拦截器
AXIOS.interceptors.request.use(
(config) => {
// 从 localStorage 中获取 token
const token = localStorage.getItem(TOKEN_KEY)
if (token) {
// 确保 headers 存在
config.headers = config.headers || {}
config.headers.Authorization = `Bearer ${token}` // 使用标准的 Bearer 认证方式
}
return config
},
(error) => {
console.error("请求错误:", error)
return Promise.reject(error)
}
)
// 响应拦截器
AXIOS.interceptors.response.use(
(response) => {
const { code, message } = response.data
switch (code) {
case ResultEnum.NO_AUTH:
// 无权限,清除 token 并重定向
localStorage.removeItem(TOKEN_KEY)
router.push('/login')
break
case ResultEnum.LOGIN_FAIL:
// 登录失败,显示消息并清除 token
ElMessage.error(message || "登录失败")
localStorage.removeItem(TOKEN_KEY)
break
case ResultEnum.SUCCESS:
// 成功响应,直接返回 data
return response.data as AxiosResponse<HttpResponse>
default:
// 其他错误状态,显示错误消息
if (message) {
ElMessage.error(message)
}
break
}
return response.data
},
(error) => {
// 响应错误的处理
if (error.response) {
ElMessage.error(`请求错误:${error.response.status}`)
} else {
ElMessage.error("网络错误,请检查您的网络连接")
}
console.error("响应错误:", error)
return Promise.reject(error)
}
)
export default AXIOS
二、代码详解
让我们一步步分析这个代码实现。
1. 创建 axios 实例
const AXIOS = axios.create({
baseURL: '/lili-api',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
这里用 axios.create()
创建了一个 axios
实例 AXIOS
。这样做的好处是可以给该实例绑定一个特定的基础 URL (baseURL
),以后所有用 AXIOS
发送的请求都会自动加上这个 URL 前缀,省去了我们在每个请求里手动写基础路径的麻烦。
2. 请求拦截器
AXIOS.interceptors.request.use(
(config) => {
const token = localStorage.getItem(TOKEN_KEY)
if (token) {
config.headers = config.headers || {}
config.headers.Authorization = `Bearer ${token}` // 使用标准的 Bearer 认证方式
}
return config
},
(error) => {
console.error("请求错误:", error)
return Promise.reject(error)
}
)
请求拦截器会在每个请求发送前执行,它主要用来:
- 检查
localStorage
是否有token
。如果有,将其添加到请求头的Authorization
字段,以Bearer
格式添加,更符合 RESTful API 认证标准。 - 添加
Content-Type
为application/json;charset=utf-8
,表示请求体的数据格式是 JSON。 - 使用
console.error
方便调试,输出请求拦截阶段的错误。
3. 响应拦截器
AXIOS.interceptors.response.use(
(response) => {
const { code, message } = response.data
switch (code) {
case ResultEnum.NO_AUTH:
localStorage.removeItem(TOKEN_KEY)
router.push('/login')
break
case ResultEnum.LOGIN_FAIL:
ElMessage.error(message || "登录失败")
localStorage.removeItem(TOKEN_KEY)
break
case ResultEnum.SUCCESS:
return response.data as AxiosResponse<HttpResponse>
default:
if (message) {
ElMessage.error(message)
}
break
}
return response.data
},
(error) => {
if (error.response) {
ElMessage.error(`请求错误:${error.response.status}`)
} else {
ElMessage.error("网络错误,请检查您的网络连接")
}
console.error("响应错误:", error)
return Promise.reject(error)
}
)
响应拦截器主要功能是根据响应的状态码做出不同处理:
ResultEnum.NO_AUTH
:无权限状态,清除token
,并重定向到登录页。ResultEnum.LOGIN_FAIL
:登录失败状态,弹出错误提示,并清除token
。ResultEnum.SUCCESS
:请求成功时,直接返回数据。- 其他错误:在
switch
语句中处理其他未知状态码,弹出错误信息提示。
同时,为了更全面地处理错误情况,响应错误 error
的处理阶段会显示更详细的状态码错误信息,或者在网络错误时给予用户相应提示。
三、面试中的常见问题
下面总结一些面试中关于 axios
拦截器的常见问题及答案示例:
1. 拦截器的作用是什么?
回答示例:拦截器允许我们在请求发出前和响应返回后插入自定义逻辑。常见用途包括在请求前检查认证 token 并添加到请求头、在响应后根据状态码处理错误、自动重定向等。拦截器可以减少代码重复,使请求的处理更集中、统一。
2. 为什么使用 axios.create
而不是直接用 axios
?
回答示例:axios.create
创建了一个独立的 axios
实例,可以在这个实例上设置独特的基础配置,比如 baseURL
、超时时间等。这种方式更模块化,适用于不同的 API,有助于项目中不同请求模块的管理。
3. 为什么要在请求头中设置 Content-Type
?
回答示例:Content-Type
告诉服务器请求体的数据格式。设置为 application/json
,表示我们发送的数据是 JSON 格式,这样服务器就知道如何解析请求数据。这种设置在 RESTful API 中是很常见的。
4. 为什么在无权限和登录失败时清除 token
?
回答示例:NO_AUTH
和 LOGIN_FAIL
表示当前的 token
已失效(过期或被篡改)。为了安全性,我们在这种情况下会清除 token
并重定向到登录页,这样可以确保用户在下一次请求前先重新登录,获取新的 token
。
5. 响应拦截器返回 response.data as AxiosResponse<HttpResponse>
的作用是什么?
回答示例:response.data as AxiosResponse<HttpResponse>
是为了确保响应数据符合 HttpResponse
类型,让代码获得类型提示和安全检查的好处。这对 TypeScript 项目尤为重要,可以及时发现类型不匹配的问题,提升代码的可靠性。
6. 如何在拦截器中实现全局的加载动画?
回答示例:可以在请求拦截器和响应拦截器中控制加载动画的显示和隐藏。在请求拦截器里显示加载动画,在响应完成或失败时隐藏它。通过全局状态管理工具(如 Vuex)控制 loading
状态,可以实现全局的加载效果。