axios
1.axios的基础使用
GET:获取
POST:添加
PUT:更新
DELETE:删除
// 引入bootstrap css样式
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet">
// 引入axios
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
const btns=document.querySelectorAll('button');
//第一个 获取id为2的数据
btns[0].onclick=function(){
// 发送ajax请求
axios({
// 请求类型
method:'GET/POST/DELETE/PUT',
// URL
url:'http://localhost:3000/posts/2',
}).then(response=>{
console.log(response);
})
}
2.axios拦截器设定
1.默认配置
axios.defaults.method='GET', // 设置默认的请求类型
axios.defaults.baseURL='http://localhost:3000', // 设置基础的url
axios.defaults.params={id:1}, // 默认参数
axios.defaults.timeout=3000;// 响应超时
2.创建实例对象
const axiosSelf = axios.create({
baseURL:'http://localhost:3000',
timeout:3000
})
3.设置请求拦截器 (可对请求参数进行设置 )
axiosSelf.interceptors.request.use(function(config){
console.log("请求成功");
// 设置 config 请求参数
config.params={a:100};
config.timeout=3000;
return config
// throw '参数出了问题';
},function(error){
console.log("请求失败");
return Promise.reject(error);
})
4.设置响应拦截器
axiosSelf.interceptors.response.use(function(config){
console.log("响应拦截器成功",config);
return config.data; // 直接返回响应体
// return config
},function(error){
console.log("响应拦截器失败",error);
return Promise.reject(error)
})
5.请求接口
axiosSelf({
url:'/comments'
}).then(response=>{
console.log("自定义",response);
}).catch(reason=>{
console.log('自定义回调失败');
})
3.axios 的请求取消
前提:接口要有响应时间(几秒后再请求)如
json-server --watch db.json -d 2000 使接口2s后请求以看到效果
1.取消请求内容
// 2.声明全局变量
let cancel = null;
btns[0].onclick=function(){
axios.request({
method:'GET',
url:'http://localhost:3000/posts',
// 1. 添加配置对象属性
cancelToken:new axios.CancelToken(function(c){
// 3.将 c 值赋值给 cancel
cancel = c;
})
}).then(response=>{
console.log(response);
})
}
//绑定第二个请求--取消请求
btns[1].onclick=function(){
cancel();
}
2 取消请求结果图片
3. 应用于一个接口被多次点击调用后只调用一次(防止接口多次请求)----接口防抖
let cancel = null;
btns[0].onclick=function(){
// 当一个接口被多次点击请求后,为了防止接口多次请求
if(cancel !== null){
cancel();
}
// 借助request
axios.request({
method:'GET',
url:'http://localhost:3000/posts',
// 1. 添加配置对象属性
cancelToken:new axios.CancelToken(function(c){
// 3.将 c 值赋值给 cancel
cancel = c;
})
}).then(response=>{
console.log(response);
cancel = null ;
})
}
//绑定第二个请求--取消请求
btns[1].onclick=function(){
cancel();
}
结果图片
axios 接口的封装
- 封装
// 统一的请求发送
import axios from 'axios'
import { message, Modal, notification } from 'ant-design-vue'
import sysConfig from '@/config/index'
import tool from '@/utils/tool'
import qs from 'qs'
import { viewTagsStore } from '@/store'
// 以下这些code需要重新登录
const reloadCodes = [401, 1011007, 1011008]
const errorCodeMap = {
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
}
// 定义一个重新登录弹出窗的变量
const loginBack = ref(false)
// 创建 axios 实例
const service = axios.create({
baseURL: '/api', // api base_url
timeout: sysConfig.TIMEOUT // 请求超时时间
})
// token 键定义
const accessTokenKey = sysConfig.ACCESS_TOEKN_KEY
const refreshAccessTokenKey = sysConfig.REFRESH_TOEKN_KEY
// 清除 token
const clearAccessTokens = () => {
tool.data.remove(accessTokenKey)
tool.data.remove(refreshAccessTokenKey)
}
// HTTP request 拦截器
service.interceptors.request.use(
(config) => {
const token = tool.data.get(accessTokenKey)
if (token) {
config.headers[sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + token
// 判断 accessToken 是否过期
const jwt = decryptJWT(token)
const exp = getJWTDate(jwt.exp)
// token 已经过期
if (new Date() >= exp) {
// 获取刷新 token
const refreshAccessToken = tool.data.get(refreshAccessTokenKey)
// 携带刷新 token
if (refreshAccessToken) {
config.headers['X-' + sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + refreshAccessToken
}
}
}
if (!sysConfig.REQUEST_CACHE && config.method === 'get') {
config.params = config.params || {}
config.params._ = new Date().getTime()
}
Object.assign(config.headers, sysConfig.HEADERS)
return config
},
(error) => {
return Promise.reject(error)
}
)
// 保持重新登录Modal的唯一性
const error = () => {
loginBack.value = true
Modal.error({
title: '提示:',
okText: '重新登录',
content: '登录已失效, 请重新登录',
onOk: () => {
const store = viewTagsStore() //获取viewTagsStore
console.log(store.viewTags)
const lastPath = store.lastPath //获取最后一个路由
//如果有lastPath
if (lastPath !== '') {
localStorage.setItem('lastVisitPath', lastPath) //记录当前路由
}
loginBack.value = false
tool.data.remove(accessTokenKey)
tool.data.remove(refreshAccessTokenKey)
tool.data.remove('USER_INFO')
tool.data.remove('MENU')
tool.data.remove('PERMISSIONS')
window.location.reload()
}
})
}
// HTTP response 拦截器
service.interceptors.response.use(
(response) => {
// 配置了blob,不处理直接返回文件流
if (response.config.responseType === 'blob') {
if (response.status === 200) {
return response
} else {
message.warning('文件下载失败或此文件不存在')
return
}
}
// 检查并存储授权信息
checkAndStoreAuthentication(response)
const data = response.data
const code = data.code
if (reloadCodes.includes(code)) {
if (!loginBack.value) {
error()
}
return
}
if (code !== 200) {
if (code === 401) {
clearAccessTokens()
}
const customErrorMessage = response.config.customErrorMessage
const ignoreError = response.config.ignoreError //如果忽略错误就不输出错误消息
if (!ignoreError) {
message.error(customErrorMessage || data.msg)
}
return Promise.reject(data)
// 自定义错误提示,覆盖后端返回的message
// 使用示例:
// export function customerList (data) {
// return request('list', data, 'get', {
// customErrorMessage: '自定义错误消息提示'
// });
// }
} else {
// 统一成功提示
const responseUrl = response.config.url
const apiNameArray = [
'add',
'edit',
'delete',
'update',
'grant',
'reset',
'start',
'stop',
'pass',
'disable',
'enable',
'revoke',
'suspend',
'active',
'turn',
'adjust',
'reject',
'copy'
]
//指定方法不提示
const noMessageApiNameArray = []
apiNameArray.forEach((apiName) => {
let responseApiArray = responseUrl.split('/') //分割
let method = responseApiArray[responseApiArray.length - 1] //取最后一个
let result = noMessageApiNameArray.includes(method)
if (!result && responseUrl.includes(apiName)) {
message.success(data.msg)
}
})
}
return Promise.resolve(data.data)
},
(error) => {
if (error) {
const status = 503
const description = errorCodeMap[status]
notification.error({
message: '请求错误',
description
})
return Promise.reject(status)
}
}
)
export const baseRequest = (url, value = {}, method = 'post', options = {}) => {
if (sysConfig.VITE_PROXY === 'false') url = sysConfig.API_URL + url //判断是否要走代理模式,走了的话发布之后直接nginx代理
if (method === 'post') {
return service.post(url, value, options)
} else if (method === 'get') {
return service.get(url, { params: value, ...options })
} else if (method === 'formdata') {
// form-data表单提交的方式
return service.post(url, qs.stringify(value), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
...options
})
} else {
return service({
method: method,
url,
data: value,
...options
})
}
}
/**
* 检查并存储授权信息
* @param res 响应对象
*/
function checkAndStoreAuthentication(res) {
// 读取响应报文头 token 信息
var accessToken = res.headers[accessTokenKey]
var refreshAccessToken = res.headers[refreshAccessTokenKey]
// 判断是否是无效 token
if (accessToken === 'invalid_token') {
clearAccessTokens()
}
// 判断是否存在刷新 token,如果存在则存储在本地
else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
tool.data.set(accessTokenKey, accessToken)
tool.data.set(refreshAccessTokenKey, refreshAccessToken)
}
}
/**
* 解密 JWT token 的信息
* @param token jwt token 字符串
* @returns <any>object
*/
function decryptJWT(token) {
token = token.replace(/_/g, '/').replace(/-/g, '+')
var json = decodeURIComponent(escape(window.atob(token.split('.')[1])))
return JSON.parse(json)
}
/**
* 将 JWT 时间戳转换成 Date
* @description 主要针对 `exp`,`iat`,`nbf`
* @param timestamp 时间戳
* @returns Date 对象
*/
function getJWTDate(timestamp) {
return new Date(timestamp * 1000)
}
// 模块内的请求, 会自动加上模块的前缀
export const moduleRequest =
(moduleUrl) =>
(url, ...arg) => {
return baseRequest(moduleUrl + url, ...arg)
}
export default service
- 使用
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/auth/${url}`, ...arg)
export default {
// 会话统计
monitorAnalysis(data) {
return request('session/analysis', data, 'get')
}
}
Axios 的 service.post() 方法的第三个参数 options 是一个可选的配置对象
,可以传递一些额外的配置项,用于控制请求行为。以下是一些常用的 options 配置项:
params:对象类型,用于指定请求的查询参数(即 URL 上的参数),默认值为 {}。
headers:对象类型,用于指定请求的头部信息。
responseType:字符串类型,用于指定响应的数据类型。可以是 ‘json’、‘text’、‘arraybuffer’、‘blob’ 等类型。
timeout:数值类型,用于指定请求的超时时间(单位:毫秒),默认值为 0,表示没有超时限制。
withCredentials:布尔类型,指示是否在跨域请求中发送凭证(即 Cookie 等敏感信息),默认值为 false。
transformRequest:为函数类型或以数组形式存储的函数类型,用于在请求数据发送到服务器之前修改请求数据。数组形式时数组的元素需要为函数类型。
transformResponse:为函数类型或以数组形式存储的函数类型,用于在响应数据传递到 then 或 catch 前修改响应数据。数组形式时数组的元素需要为函数类型。
还有其他一些可选配置项,具体可以查看 Axios 的官方文档。
下面是一个示例代码,演示如何使用 options 配置项:
const service = axios.create({
baseURL: '/api',
timeout: 5000
});
service.post('/login', {
username: 'admin',
password: '123456'
}, {
headers: {
'Content-Type': 'application/json'
},
timeout: 3000
}).then(response => {
console.log(response.data);
}).catch(error => {
console.error(error);
});