Axios 中文文档
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。在浏览器中创建 XMLHttpRequests,在 node.js 创建 http 请求。
特性
- ✅ 支持 Promise API
- ✅ 拦截请求和响应
- ✅ 转换请求数据和响应数据
- ✅ 取消请求(支持 CancelToken 和 AbortController)
- ✅ 自动转换 JSON 数据
- ✅ 客户端支持防御 XSRF
- ✅ 支持上传/下载进度监控
- ✅ 支持请求/响应超时设置
- ✅ 支持 TypeScript
兼容性
axios 依赖原生的 ES6 Promise 实现而被支持。如果你的环境不支持 ES6 Promise,你可以使用 polyfill。
安装使用
安装
# 使用 npm
npm install axios
# 使用 yarn
yarn add axios
# 使用 pnpm
pnpm add axios
# 使用 CDN
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>引入
// ES6 模块
import axios from "axios"
// CommonJS
const axios = require("axios")
// 浏览器环境(通过 CDN)
// axios 会自动挂载到 window 对象上基本用法
GET 请求
使用 Axios 执行 GET 请求有多种方式:
// 方式 1: 使用 URL 参数
axios
.get("/user?ID=12345")
.then(function (response) {
console.log(response.data)
})
.catch(function (error) {
console.log(error)
})
// 方式 2: 使用 params 配置(推荐)
axios
.get("/user", {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response.data)
})
.catch(function (error) {
console.log(error)
})
// 方式 3: 使用 async/await(推荐)
async function getUser() {
try {
const response = await axios.get("/user", {
params: { ID: 12345 }
})
console.log(response.data)
} catch (error) {
console.error(error)
}
}POST 请求
// 方式 1: 使用 Promise
axios
.post("/user", {
firstName: "Fred",
lastName: "Flintstone"
})
.then(function (response) {
console.log(response.data)
})
.catch(function (error) {
console.log(error)
})
// 方式 2: 使用 async/await(推荐)
async function createUser() {
try {
const response = await axios.post("/user", {
firstName: "Fred",
lastName: "Flintstone"
})
console.log(response.data)
} catch (error) {
console.error(error)
}
}PUT、PATCH、DELETE 请求
// PUT 请求 - 更新整个资源
axios.put("/user/12345", {
firstName: "Fred",
lastName: "Flintstone"
})
// PATCH 请求 - 部分更新资源
axios.patch("/user/12345", {
firstName: "Fred"
})
// DELETE 请求
axios.delete("/user/12345")并发请求
执行多个并发请求可以使用 Promise.all 或 axios.all:
// 方式 1: 使用 Promise.all(推荐)
async function getMultipleData() {
try {
const [userResponse, permissionsResponse] = await Promise.all([axios.get("/user/12345"), axios.get("/user/12345/permissions")])
console.log(userResponse.data)
console.log(permissionsResponse.data)
} catch (error) {
console.error(error)
}
}
// 方式 2: 使用 axios.all(已废弃,建议使用 Promise.all)
function getUserAccount() {
return axios.get("/user/12345")
}
function getUserPermissions() {
return axios.get("/user/12345/permissions")
}
axios.all([getUserAccount(), getUserPermissions()]).then(
axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
console.log(acct.data)
console.log(perms.data)
})
)axios API
可以通过向 axios 传递相关配置来创建请求
// 发送 POST 请求
axios({
method: "post",
url: "/user/12345",
data: {
firstName: "Fred",
lastName: "Flintstone"
}
})
// 获取远端图片
axios({
method: "get",
url: "http://bit.ly/2mTM3nY",
responseType: "stream"
}).then(function (response) {
response.data.pipe(fs.createWriteStream("ada_lovelace.jpg"))
})
// 发送 GET 请求(默认的方法)
axios("/user/12345")请求方法的别名
为方便起见,为所有支持的请求方法提供了别名
-
axios.request(config)
-
axios.get(url[, config])
-
axios.delete(url[, config])
-
axios.head(url[, config])
-
axios.options(url[, config])
-
axios.post(url[, data[, config]])
-
axios.put(url[, data[, config]])
-
axios.patch(url[, data[, config]])
在使用别名方法时 url、method、data 这些属性都不必在配置中指定
并发
处理并发请求的助手函数
-
axios.all(iterable)
-
axios.spread(callback)
// 引入axios库
const axios = require("axios")
// 定义两个请求
const request1 = axios.get("https://api.example.com/user?ID=12345")
const request2 = axios.get("https://api.example.com/user?ID=67890")
// 使用 axios.all 发送请求
axios
.all([request1, request2])
.then(
axios.spread((response1, response2) => {
// response1 和 response2 是两个请求的响应对象
console.log(response1.data)
console.log(response2.data)
})
)
.catch((error) => {
// 捕获并处理错误
console.error(error)
})axios.create([config])
可以使用自定义配置新建一个 axios 实例
const instance = axios.create({
baseURL: "https://some-domain.com/api/",
timeout: 1000,
headers: { "X-Custom-Header": "foobar" }
})请求配置
创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法
{
url: '/user',
method: 'get', // default
// baseURL 将自动加在 url 前面,除非 url 是一个绝对 URL。
baseURL: 'https://some-domain.com/api/',
// transformRequest 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [function (data, headers) {
// 对 data 进行任意转换处理
return data;
}],
// transformResponse 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// headers 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// params 是即将与请求一起发送的 URL 参数,必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// paramsSerializer 是一个负责 params 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// data 是作为请求主体被发送的数据,只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 transformRequest 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// timeout 指定请求超时的毫秒数(0 表示无超时时间),如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// withCredentials 表示跨域请求时是否需要使用凭证
withCredentials: false, // default
// adapter 允许自定义处理请求,以使测试更轻松
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
adapter: function (config) {
/* ... */
},
// auth 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 Authorization 头,覆写掉现有的任意使用 headers 设置的自定义 Authorization 头
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// responseType 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
// `responseEncoding` indicates encoding to use for decoding responses
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default
// xsrfCookieName 是用作 xsrf token 的值的 cookie 的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// xsrfHeaderName is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// onUploadProgress 允许为上传处理进度事件
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},
// onDownloadProgress 允许为下载处理进度事件
onDownloadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// maxContentLength 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// validateStatus 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。
// 如果 validateStatus 返回 true (或者设置为 null 或 undefined),promise 将被 resolve;
// 否则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// maxRedirects 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // default
// `socketPath` defines a UNIX Socket to be used in node.js.
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
// Only either `socketPath` or `proxy` can be specified.
// If both are specified, `socketPath` is used.
socketPath: null, // default
// httpAgent 和 httpsAgent 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。
// 允许像这样配置选项:
// keepAlive 默认没有启用
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// proxy 定义代理服务器的主机名称和端口
// auth 表示 HTTP 基础验证应当用于连接代理,并提供凭据
// 这将会设置一个 Proxy-Authorization 头,覆写掉已有的通过使用 header
// 设置的自定义 Proxy-Authorization 头。
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// cancelToken 指定用于取消请求的 cancel token
// 查看后面的 Cancellation 这节了解更多)
cancelToken: new CancelToken(function (cancel) {
})
}响应结构
某个请求的响应包含以下信息
{
data: {}, // data 由服务器提供的响应
status: 200, // status 来自服务器响应的 HTTP 状态码
statusText: 'OK', // statusText 来自服务器响应的 HTTP 状态信息
headers: {}, // headers 服务器响应的头
// config 是为请求提供的配置信息
config: {},
// 'request'
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}使用 then 时,你将接收下面这样的响应 :
axios.get("/user/12345").then(function (response) {
console.log(response.data)
console.log(response.status)
console.log(response.statusText)
console.log(response.headers)
console.log(response.config)
})配置默认值
全局的 axios 默认值
axios.defaults.baseURL = "https://api.example.com"
axios.defaults.headers.common["Authorization"] = AUTH_TOKEN
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"自定义实例默认值
const instance = axios.create({
baseURL: "https://api.example.com"
})
// Alter defaults after instance has been created
instance.defaults.headers.common["Authorization"] = AUTH_TOKEN配置的优先顺序
配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者
// 使用由库提供的配置的默认值来创建实例,此时超时配置的默认值是 `0`
var instance = axios.create()
// 覆写库的超时默认值。现在在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500
// 为已知需要花费很长时间的请求覆写超时设置
instance.get("/longRequest", {
timeout: 5000
})拦截器
在请求或响应被 then 或 catch 处理前拦截它们。拦截器是 Axios 非常强大的功能,常用于添加认证 token、统一处理错误、添加 loading 状态等。
拦截器基本用法
// 添加请求拦截器
axios.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
return config
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 对响应数据做点什么
return response
},
function (error) {
// 对响应错误做点什么
return Promise.reject(error)
}
)实际应用场景
1. 添加认证 Token
// 请求拦截器:添加 token
axios.interceptors.request.use(
function (config) {
const token = localStorage.getItem("token")
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
function (error) {
return Promise.reject(error)
}
)2. 统一处理响应数据
// 响应拦截器:统一处理响应格式
axios.interceptors.response.use(
function (response) {
// 假设后端返回格式为 { code: 200, data: {}, message: '' }
const { code, data, message } = response.data
if (code === 200) {
return data // 直接返回 data 部分
} else {
return Promise.reject(new Error(message || "请求失败"))
}
},
function (error) {
return Promise.reject(error)
}
)3. 统一错误处理
// 响应拦截器:统一错误处理
axios.interceptors.response.use(
function (response) {
return response
},
function (error) {
if (error.response) {
// 服务器返回了错误状态码
switch (error.response.status) {
case 401:
// 未授权,跳转到登录页
window.location.href = "/login"
break
case 403:
console.error("没有权限访问")
break
case 404:
console.error("请求的资源不存在")
break
case 500:
console.error("服务器错误")
break
default:
console.error("请求失败")
}
} else if (error.request) {
// 请求已发出,但没有收到响应
console.error("网络错误,请检查网络连接")
} else {
// 在设置请求时发生了错误
console.error("请求配置错误", error.message)
}
return Promise.reject(error)
}
)4. 添加 Loading 状态
let loadingCount = 0
// 请求拦截器:显示 loading
axios.interceptors.request.use(
function (config) {
loadingCount++
if (loadingCount === 1) {
// 显示 loading
showLoading()
}
return config
},
function (error) {
loadingCount--
if (loadingCount === 0) {
hideLoading()
}
return Promise.reject(error)
}
)
// 响应拦截器:隐藏 loading
axios.interceptors.response.use(
function (response) {
loadingCount--
if (loadingCount === 0) {
hideLoading()
}
return response
},
function (error) {
loadingCount--
if (loadingCount === 0) {
hideLoading()
}
return Promise.reject(error)
}
)5. 请求重试
axios.interceptors.response.use(
function (response) {
return response
},
async function (error) {
const config = error.config
// 如果已经重试过,直接拒绝
if (config.__retryCount >= 3) {
return Promise.reject(error)
}
// 设置重试次数
config.__retryCount = config.__retryCount || 0
config.__retryCount++
// 等待一段时间后重试
await new Promise((resolve) => setTimeout(resolve, 1000))
return axios(config)
}
)移除拦截器
如果你想在稍后移除拦截器,可以这样:
const myInterceptor = axios.interceptors.request.use(function () {
/*...*/
})
// 移除拦截器
axios.interceptors.request.eject(myInterceptor)为自定义实例添加拦截器
const instance = axios.create({
baseURL: "https://api.example.com"
})
instance.interceptors.request.use(function (config) {
// 自定义实例的请求拦截器
return config
})
instance.interceptors.response.use(function (response) {
// 自定义实例的响应拦截器
return response
})错误处理
axios.get("/user/12345").catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request)
} else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message)
}
console.log(error.config)
})可以使用 validateStatus 配置选项定义一个自定义 HTTP 状态码的错误范围
axios.get("/user/12345", {
validateStatus: function (status) {
return status < 500 // Reject only if the status code is greater than or equal to 500
}
})取消请求
Axios 支持两种方式取消请求:
- CancelToken(已废弃,但仍可使用)
- AbortController(推荐,现代浏览器和 Node.js 18+ 支持)
使用 AbortController(推荐)
AbortController 是现代浏览器和 Node.js 18+ 原生支持的标准 API,推荐使用:
// 创建 AbortController
const controller = new AbortController()
// 发送请求
axios
.get("/user/12345", {
signal: controller.signal
})
.then(function (response) {
console.log(response.data)
})
.catch(function (error) {
if (axios.isCancel(error)) {
console.log("请求已取消", error.message)
} else {
console.error("请求错误", error)
}
})
// 取消请求
controller.abort("操作被用户取消")使用 CancelToken(已废弃)
虽然已废弃,但在旧版本中仍可使用:
const CancelToken = axios.CancelToken
const source = CancelToken.source()
axios
.get("/user/12345", {
cancelToken: source.token
})
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log("请求已取消", thrown.message)
} else {
// 处理错误
}
})
// 取消请求(message 参数是可选的)
source.cancel("操作被用户取消")还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:
const CancelToken = axios.CancelToken
let cancel
axios.get("/user/12345", {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c
})
})
// 取消请求
cancel("操作被用户取消")取消请求的实际应用场景
1. 取消重复请求
const pendingRequests = new Map()
function requestWithCancel(url, config = {}) {
// 如果存在相同的请求,先取消
if (pendingRequests.has(url)) {
pendingRequests.get(url).abort()
}
const controller = new AbortController()
pendingRequests.set(url, controller)
return axios
.get(url, {
...config,
signal: controller.signal
})
.finally(() => {
pendingRequests.delete(url)
})
}2. 组件卸载时取消请求
// React 示例
import { useEffect, useRef } from "react"
function UserProfile({ userId }) {
const controllerRef = useRef(null)
useEffect(() => {
controllerRef.current = new AbortController()
axios
.get(`/user/${userId}`, {
signal: controllerRef.current.signal
})
.then((response) => {
// 处理响应
})
// 组件卸载时取消请求
return () => {
controllerRef.current?.abort()
}
}, [userId])
return <div>...</div>
}3. 取消多个请求
const controller = new AbortController()
// 使用同一个 controller 可以取消多个请求
Promise.all([
axios.get("/user/12345", { signal: controller.signal }),
axios.get("/user/12345/permissions", { signal: controller.signal }),
axios.get("/user/12345/settings", { signal: controller.signal })
]).catch((error) => {
if (axios.isCancel(error)) {
console.log("所有请求已取消")
}
})
// 取消所有请求
controller.abort()注意: 可以使用同一个 cancel token 或 AbortController 取消多个请求
文件上传/下载
文件上传
1. 单文件上传
// 方式 1: 使用 FormData
const formData = new FormData()
const fileInput = document.querySelector('input[type="file"]')
formData.append("file", fileInput.files[0])
axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: function (progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`上传进度: ${percentCompleted}%`)
}
})2. 多文件上传
const formData = new FormData()
const files = document.querySelector('input[type="file"]').files
for (let i = 0; i < files.length; i++) {
formData.append("files", files[i])
}
axios.post("/upload/multiple", formData, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: function (progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`上传进度: ${percentCompleted}%`)
}
})3. 带其他数据的文件上传
const formData = new FormData()
formData.append("file", fileInput.files[0])
formData.append("userId", "12345")
formData.append("description", "文件描述")
axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})4. 使用 async/await
async function uploadFile(file) {
const formData = new FormData()
formData.append("file", file)
try {
const response = await axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`上传进度: ${percentCompleted}%`)
}
})
console.log("上传成功", response.data)
} catch (error) {
console.error("上传失败", error)
}
}文件下载
1. 下载文件(浏览器)
// 方式 1: 使用 blob
axios
.get("/download/file.pdf", {
responseType: "blob"
})
.then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement("a")
link.href = url
link.setAttribute("download", "file.pdf")
document.body.appendChild(link)
link.click()
link.remove()
window.URL.revokeObjectURL(url)
})
// 方式 2: 使用 arraybuffer
axios
.get("/download/file.pdf", {
responseType: "arraybuffer",
onDownloadProgress: function (progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`下载进度: ${percentCompleted}%`)
}
})
.then((response) => {
const blob = new Blob([response.data], { type: "application/pdf" })
const url = window.URL.createObjectURL(blob)
const link = document.createElement("a")
link.href = url
link.setAttribute("download", "file.pdf")
document.body.appendChild(link)
link.click()
})2. 下载文件(Node.js)
const fs = require("fs")
const axios = require("axios")
axios({
method: "get",
url: "http://example.com/file.pdf",
responseType: "stream"
}).then((response) => {
response.data.pipe(fs.createWriteStream("file.pdf"))
})使用 application/x-www-form-urlencoded format
默认情况下 axios 将 JavaScript 对象序列化为 JSON。但是要以 application/x-www-form-urlencoded 格式发送数据,可以使用以下选项之一
浏览器
在浏览器中可以使用 URLSearchParams API,以 application/x-www-form-urlencoded 格式发送数据
const params = new URLSearchParams()
params.append("param1", "value1")
params.append("param2", "value2")
axios.post("/foo", params)请注意,所有浏览器都不支持 URLSearchParams(请参阅 caniuse.com),但可以使用 polyfill(确保填充全局环境)
或者,使用 qs 库编码数据
const qs = require("qs")
axios.post("/foo", qs.stringify({ bar: 123 }))或者以另一种方式 ES6
import qs from "qs"
const data = { bar: 123 }
const options = {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
data: qs.stringify(data),
url
}
axios(options)Node.js
可以使用 querystring 模块,也可以使用 qs 库
const querystring = require("querystring")
axios.post("http://something.com/", querystring.stringify({ foo: "bar" }))TypeScript
axios 内置了完整的 TypeScript 类型定义,提供了良好的类型支持。
基本使用
import axios from "axios"
// 基本请求
axios.get("/user?ID=12345")
// 带类型推断
interface User {
id: number
name: string
email: string
}
axios.get<User>("/user/12345").then((response) => {
// response.data 的类型是 User
console.log(response.data.name)
})定义响应类型
interface ApiResponse<T> {
code: number
data: T
message: string
}
interface User {
id: number
name: string
email: string
}
// 使用泛型定义响应类型
axios.get<ApiResponse<User>>("/user/12345").then((response) => {
// response.data 的类型是 ApiResponse<User>
const user = response.data.data
console.log(user.name)
})自定义请求配置类型
import axios, { AxiosRequestConfig, AxiosResponse } from "axios"
interface CustomRequestConfig extends AxiosRequestConfig {
showLoading?: boolean
retry?: number
}
const customConfig: CustomRequestConfig = {
url: "/user/12345",
method: "get",
showLoading: true,
retry: 3
}
axios(customConfig)扩展 Axios 实例类型
import axios, { AxiosInstance, AxiosRequestConfig } from "axios"
interface CustomConfig extends AxiosRequestConfig {
showLoading?: boolean
}
interface CustomInstance extends AxiosInstance {
get<T = any>(url: string, config?: CustomConfig): Promise<T>
post<T = any>(url: string, data?: any, config?: CustomConfig): Promise<T>
}
const instance: CustomInstance = axios.create({
baseURL: "https://api.example.com"
}) as CustomInstance
// 使用
instance.get<User>("/user/12345", { showLoading: true })拦截器类型
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"
// 请求拦截器
axios.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 添加 token
const token = localStorage.getItem("token")
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
// 响应拦截器
axios.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
(error: AxiosError) => {
if (error.response) {
switch (error.response.status) {
case 401:
// 处理未授权
break
case 500:
// 处理服务器错误
break
}
}
return Promise.reject(error)
}
)错误处理类型
import axios, { AxiosError } from "axios"
axios.get("/user/12345").catch((error: AxiosError) => {
if (error.response) {
// 服务器返回了错误状态码
console.error("响应错误:", error.response.status)
console.error("错误数据:", error.response.data)
} else if (error.request) {
// 请求已发出,但没有收到响应
console.error("请求错误:", error.request)
} else {
// 在设置请求时发生了错误
console.error("错误:", error.message)
}
})最佳实践
1. 创建 Axios 实例
为不同的 API 创建不同的实例,避免全局配置污染:
// api.js
import axios from "axios"
// 创建主 API 实例
const api = axios.create({
baseURL: "https://api.example.com",
timeout: 10000,
headers: {
"Content-Type": "application/json"
}
})
// 创建文件上传实例
const uploadApi = axios.create({
baseURL: "https://api.example.com",
timeout: 30000,
headers: {
"Content-Type": "multipart/form-data"
}
})
export { api, uploadApi }2. 统一错误处理
// 响应拦截器统一处理错误
api.interceptors.response.use(
(response) => response,
(error) => {
// 统一错误处理逻辑
const message = error.response?.data?.message || error.message || "请求失败"
// 可以在这里统一显示错误提示
// showToast(message)
return Promise.reject(error)
}
)3. 使用环境变量
// config.js
const API_BASE_URL = process.env.VUE_APP_API_BASE_URL || "https://api.example.com"
const api = axios.create({
baseURL: API_BASE_URL
})4. 封装请求方法
// request.js
import axios from "axios"
const request = {
get(url, params, config = {}) {
return axios.get(url, { params, ...config })
},
post(url, data, config = {}) {
return axios.post(url, data, config)
},
put(url, data, config = {}) {
return axios.put(url, data, config)
},
delete(url, config = {}) {
return axios.delete(url, config)
}
}
export default request5. 请求和响应数据转换
// 请求拦截器:转换请求数据
api.interceptors.request.use((config) => {
// 可以在这里对请求数据进行转换
if (config.data && typeof config.data === "object") {
// 例如:添加时间戳
config.data.timestamp = Date.now()
}
return config
})
// 响应拦截器:转换响应数据
api.interceptors.response.use((response) => {
// 统一处理响应数据格式
if (response.data.code === 200) {
return response.data.data
} else {
return Promise.reject(new Error(response.data.message))
}
})6. 避免内存泄漏
在组件卸载时取消请求:
// React 示例
useEffect(() => {
const controller = new AbortController()
api.get("/data", { signal: controller.signal }).then((response) => {
// 处理响应
})
return () => {
controller.abort()
}
}, [])常见问题
1. CORS 跨域问题
问题: 浏览器报 CORS 错误
解决方案:
- 后端需要设置正确的 CORS 头
- 开发环境可以使用代理
- 生产环境需要后端支持 CORS
// 开发环境代理配置(Vue CLI)
// vue.config.js
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
pathRewrite: {
"^/api": ""
}
}
}
}
}2. 请求参数为数组时的序列化
问题: 数组参数序列化格式不符合后端要求
解决方案: 使用 paramsSerializer
import qs from "qs"
axios.get("/api/users", {
params: {
ids: [1, 2, 3]
},
paramsSerializer: (params) => {
return qs.stringify(params, { arrayFormat: "brackets" })
}
})3. 请求超时处理
问题: 请求超时后如何处理
解决方案: 设置合理的超时时间并处理超时错误
const api = axios.create({
timeout: 10000
})
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.code === "ECONNABORTED") {
console.error("请求超时")
// 可以在这里显示超时提示
}
return Promise.reject(error)
}
)4. 重复请求问题
问题: 快速点击导致重复请求
解决方案: 使用防抖或取消重复请求
const pendingRequests = new Map()
function requestWithCancel(url, config = {}) {
if (pendingRequests.has(url)) {
pendingRequests.get(url).abort()
}
const controller = new AbortController()
pendingRequests.set(url, controller)
return axios
.get(url, {
...config,
signal: controller.signal
})
.finally(() => {
pendingRequests.delete(url)
})
}5. 文件上传时 Content-Type 问题
问题: 手动设置 Content-Type: multipart/form-data 可能导致边界丢失
解决方案: 让浏览器自动设置 Content-Type
// ❌ 错误:手动设置 Content-Type
axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data" // 不要手动设置
}
})
// ✅ 正确:让浏览器自动设置
axios.post("/upload", formData)
// 或者只设置其他头部
axios.post("/upload", formData, {
headers: {
Authorization: "Bearer token"
}
})6. 响应数据不是 JSON 格式
问题: 后端返回的不是 JSON,导致解析失败
解决方案: 设置正确的 responseType
// 文本响应
axios.get("/api/data", {
responseType: "text"
})
// Blob 响应
axios.get("/api/file", {
responseType: "blob"
})
// ArrayBuffer 响应
axios.get("/api/binary", {
responseType: "arraybuffer"
})7. 请求拦截器中异步操作
问题: 在请求拦截器中使用异步操作
解决方案: 返回 Promise
axios.interceptors.request.use(async (config) => {
// 异步获取 token
const token = await getToken()
config.headers.Authorization = `Bearer ${token}`
return config
})8. 取消请求的判断
问题: 如何判断请求是否被取消
解决方案: 使用 axios.isCancel()
axios
.get("/api/data", {
signal: controller.signal
})
.catch((error) => {
if (axios.isCancel(error)) {
console.log("请求已取消")
} else {
console.error("请求错误", error)
}
})