axios之基本封装

发布时间 2023-12-26 16:18:38作者: 一米五怎么你了

1. axios实例

安装axios库 npm install axios or yarn add axiosor CDN <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

// 引入
import axios from 'axios' // 库
import Cookies from 'js-cookie'
import { Notification, MessageBox, Message, Loading } from 'element-ui'
import { getToken } from '@/utils/auth' // 分别暴露

// 创建axios实例
const service = axios.create({
	baseURL: process.env.VUE_APP_BASE_API,
	timeout:, // 设置网络超时时间(不同于建立连接超时,而是多久得不到后台的返回数据)
	// ...
})

// ...

export default service 

2. 请求拦截器

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 1. 配置用户标识,需要判断本地是否存在 token,每个请求都带上给后端验证 token(失效or过期)
    if (getToken()) { // localStorage.sessionId
      config.headers['Authorization'] = getToken() 
    }
    // 2.调整请求
	// 配置请求头,请求体中的数据会以 json 字符串的形式发送到后端,axios请求类型默认就是这个编码格式,http默认的是application/x-www-form-urlencoded
    config.headers['Content-Type'] = '’application/json;charset=utf-8' // 会进行JSON序列化( JS 对象 ==> JSON 字符串 )
    // 3. 根据需求添加请求头
	if () {
	  config.headers['xxx'] = Cookies.get('xxx')
	}
    return config
  },
  error => {
	  // 暂时没找到【主动】触发第二个回调的方法
	  Promise.reject(error)
  }
)

auth.js

import Cookies from 'js-cookie'
import Config from '@/settings'

const TokenKey = Config.TokenKey
export function getToken() {
  return Cookies.get(TokenKey)
}

2. 响应拦截器的两个函数参数的参数

2.1 response对象的结构

ajax请求对象

response: {
	config:{}, // 请求配置对象
	headers:{}, // 响应头
	request:{
		readyState:4,// 0/1/2/3/4
		responseType:'',
		status:200,
		statusText:''
	} // 原生ajax请求对象
	statusText:'',
	status:200, // 响应状态码,http协议规定
	data:{
		{  
			code:0, // 业务状态码,后端约定
			data:{},
			message:''
		 }
	}, // data 才是后台接口代码中的 return 值!
}

2.2 error对象的结构

{
	message:'', // 错误信息
	code:'', // 网络超时会有这个属性,被设置为ECONNABORTED
	response:{}, // 非网络超时才有,响应对象,同上
	// ...
}

3. 响应拦截器

// 响应拦截器
service.interceptors.response.use(
	response => {
		// 只要请求响应的状态码,response.status 为 2xx,即会触发这个回调
		// 二进制数据则直接返回
	    if(response.request.responseType ===  'blob' || response.request.responseType ===  'arraybuffer'){
	      return response.data 
	    }
		// 注意,如登录:输入的用户密码错误时,请求响应成功,状态码是200,一般是response.data里设置code或success字段,用0/1来表示业务上的成功与否。
		// 此处就这样返回,具体业务中就需要每次都逻辑判断code或success的情况
		// return response.data
		// 此处假设业务状态码的键为code,0为成功、1为失败;业务消息为msg,因为进到这个回调里的肯定是2xx,所以这里处理的是业务or逻辑上的错误
		// 怎么感觉没有什么必要啊,不同的业务不是会有不同的处理吗
		const { code, msg, data } = response.data || {}
		if() {
			return Promise.reject('error')
			Message.error(msg ? msg : 'xxx') 
		} else if() {
			return Promise.reject('error')
			Message.error(msg ? msg : 'xxx') 
		} else {
			return data 
		}
	},
	error => {
		// 其它情况
		if(){
		}
		// 请求完全得不到响应,如网络超时(timeout)会触发这个回调
		// 请求响应的状态码超出2xx,如404、500...会触发这个回调
		else {
			try {
				// 拿到响应状态码
				const status = error.response.status
			} catch(err) {
				// axios中,网络超时(timeout),error 对象只有 code 和 message 属性,因此 error.response.status 会报错,需捕获异常
				if(error.code === 'ECONNABORTED' || error.message === "Network Error" ||  error.message.includes("timeout"){
					Message({
						message: '网络超时,请稍后重试',
						type: 'error',
						duration: 3000
					})
				}
			}
			let errMsg = '未知错误' 
			switch(status){
				case 401:
					errMsg = '未授权,请重新登录'
					// 授权错误:登录用户的token无or无效or过期
					// 登出用户、跳转至登录页
					// ...
					break;
				case 403:
					errMsg = '拒绝访问' // 与用户访问服务器资源权限有关
					break;
				case 408:
					errMsg = '请求超时' // 指的是服务器等候请求时发生超时
					break;	
				case 500:
					errMsg = '服务端出错'
					break;
				case 502:
					errMsg = '网关错误'
					break;
				default:
					errMsg = `其它连接错误:${status}`
					break;
				
			}
			Message.error(errMsg)
		}
		// 取消请求,如CancelToken,可以用axios.isCancel(err)来判断是否是取消的请求
		else if(axios.isCancel(err)) {
			// 处理
		}
		// 请求运行有异常也会进入这里,如故意将headers写错:axios.defaults.headers = '123',或者在request中有语法或解析错误也会进入这里
		// ...
		// 以上的所有情况都要返回一个reject的Promise
		return Promise.reject(error)
	}
)

JSON 是 JS 对象的字符串表示法,JS对象转JSON:JSON.stringify(obj) ,JSON转JS :JSON.parse(text); 也可以用于深拷贝对象

4. 接口调用

// 接口
 export function getList(form) {
	 return request({
		 url:'',
		 method:'',
		 data:form
	 })
 }

// 调用
// getList() === > request() 函数执行,可以理解为new Promise了 === > 拦截器相当于链式调用 ==> res接收响应拦截器设置的返回值 data,err接收Promise.reject(error)
getList().then(res => {
	console.log(res) // { code:0,data:{},msg:'返回成功!'}
}).catch(err => {
	// 响应拦截器 return Promise.reject(error)就会走catch,或者是代码错误(then阶段处理逻辑代码出错也会catch哦)
	console.log(err) // 捕获响应拦截器的error对象,一般情况下如果响应拦截器对错误处理进行了封装,可以不捕获异常,除非then回调里可能出现问题~
})

附:Promise.then().catch() 和 第二个回调的区别

主要区别就是:如果在 .then 的第一个函数里抛出的异常,后面的 .catch 能捕获到,而 .then 的第二个函数捕获不到;

  1. resolve 后的,一定会进入 then 的第一个回调,肯定不会进入 catch
  2. reject 后的,一定会进入 then 中的第二个回调,如果 then 中没有写第二个回调,则进入 catch
  3. new Promise 时抛出的异常,如果 .then 有第二个回调,是会就近捕获的
  4. then 第一个回调内抛出的异常,只有 catch 可以捕获
// 4示例
const p1 = new Promise((resolve, reject) => {
  resolve('成功!');
});

p1.then(
  (value) => {
    console.log(value); // "成功!"
    throw new Error('噢,不!');
  },
  (err) => {
    console.log(err.message, 'then2');
  }
).catch((e) => {
  console.error(e.message, 'catch'); // "噢,不!"
});
// 3示例
const p1 = new Promise((resolve, reject) => {
  throw new Error('errrr');
  resolve('成功!');
});

p1.then(
  (value) => {
    console.log(value); 
  },
  (err) => {
    console.log(err.message, 'then2'); // errrr then2
  }
).catch((e) => {
  console.error(e.message, 'catch'); 
});

附:axios的post请求像get请求一样,不在请求体中携带数据而是在路径中传参

getById(id){
	return request({
		  url:`xxx/getById`, // get 是 `/${}`
		  method: 'get',
		  params:{
			id // post
		  }
	})
},