简介
在 Vue3 中,Axios通常用于封装同域名的接口请求,并且可以方便地添加公共参数。但有时候我们也需要处理不同域名的接口请求,同时自定义异常处理方法和请求头参数。这可以通过自定义 Axios 实例、拦截器和全局方法来实现
分析
Axios 我们通常用于封装同域名,公共参数的方式,那么就是一个实例 如下代码:
js
// 在 src/utils/axios.js 文件中封装 Axios 实例和全局方法
import axios from 'axios';
// 创建 Axios 实例
const instance = axios.create({
// 设置默认的 baseURL
baseURL: 'http://example.com',
// 可根据需要添加其他配置
});
// 自定义异常处理方法
function handleRequestError(error) {
// 根据错误类型进行处理,例如统一拦截错误信息,进行提示等操作
console.error('请求发生错误:', error);
}
// 设置请求头参数
function setHeaders(headers) {
// 根据需要设置请求头参数,例如 Authorization 等
instance.defaults.headers.common['Authorization'] = headers.authorization;
instance.defaults.headers.common['X-Custom-Header'] = headers.customHeader;
}
// 添加请求拦截器
instance.interceptors.request.use(
(config) => {
// 在请求发送前,可以对请求进行一些预处理,例如设置请求头参数
setHeaders(config.headers);
return config;
},
(error) => {
// 请求发送失败时的处理
handleRequestError(error);
return Promise.reject(error);
}
);
// 添加响应拦截器
instance.interceptors.response.use(
(response) => {
// 对响应数据进行处理,例如统一拦截响应数据,进行处理或转换等操作
return response;
},
(error) => {
// 响应错误处理
handleRequestError(error);
return Promise.reject(error);
}
);
// 导出全局方法
export function request(config) {
return instance.request(config);
}
我们只需要拿到instance实例就可以,操作axios提供的如下方法:
- axios(config):发送一个 HTTP 请求,并返回一个 Promise 对象,用于处理响应。
- axios.get(url[, config]):发送一个 GET 请求。
- axios.post(url[, data[, config]]):发送一个 POST 请求。
- axios.put(url[, data[, config]]):发送一个 PUT 请求。
- axios.delete(url[, config]):发送一个 DELETE 请求。
- axios.head(url[, config]):发送一个 HEAD 请求。
- axios.patch(url[, data[, config]]):发送一个 PATCH 请求。
那么我们又如何实现不同域名接口请求,自定义异常处理方法和请求头参数。
实战
我们可以创建一个 Axios 实例,并在实例化时通过设置 baseURL 来指定不同的域名。然后,我们可以在请求拦截器中自定义请求头参数,包括身份认证信息等。同时,在响应拦截器中,我们可以自定义处理响应数据,并进行统一的异常处理。最后,我们将该实例封装为一个全局方法,使得在整个应用程序中可以方便地使用该方法进行不同域名的接口请求。
通过这种方式,我们可以灵活处理不同域名的接口请求,并自定义异常处理方法和请求头参数,以满足不同的业务需求。这种封装方式可以提高代码复用性、可维护性和开发效率,使得我们在处理网络请求时更加灵活和高效。 代码如下:
js
/**
* 创建 axios 实例
* @param baseUrl 默认 url
* @param reqInterceptor 公共请求参数注入处理, 默认不需要传。如果模块需要自定义,可以传入自定义的函数
* @param onRejected 响应错误处理
* @returns
*/
export function createApiInstance(
baseUrl: string,
reqInterceptor?: RequestIntercept,
onRejected?: (error: AxiosError, commonErrMsg: string) => any
) {
// 是否正在请求刷新token接口的标记
let isRefreshing = false;
// 请求队列
let requests = [];
// 创建一个axios实例
const instance = axios.create({
baseURL: baseUrl,
timeout: 5000
});
// 请求结果拦截器
instance.interceptors.response.use(
(response) => response,
(error) => {
let msg = "";
if (error.code === "ERR_NETWORK") {
// @ts-ignore
MessagePlugin.error(i18n.global.t("zx.other.networkError"));
return Promise.reject(error);
}
switch (error.response.status) {
case 301:
msg = "资源已被移除";
break;
case 303:
msg = "重定向";
break;
case 304:
msg = "资源没有被修改";
break;
case 400:
// msg = '请求参数有误';
break;
// break;
case 401:
const { config } = error;
if (!isRefreshing) {
isRefreshing = true;
return refreshTokens()
.then((res) => {
console.log(res, "imresqqqqqqqqqqssssssseees");
setAccesstoken(res.jwt);
setRefreshCode(res.refresh_token);
config.headers.Authorization = `Bearer ${res.jwt}`;
requests.forEach((cb) => cb(res.jwt));
requests = [];
return instance(config);
})
.catch(() => {
console.log(config, "imw88888888888");
})
.finally(() => {
isRefreshing = false;
});
}
// 正在刷新token,返回一个未执行resolve的promise
return new Promise((resolve) => {
// 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
requests.push((token) => {
config.headers.Authorization = `Bearer ${token}`;
resolve(instance(config));
});
});
case 403:
quit();
break;
case 404:
msg = "资源,服务未找到";
break;
case 405:
msg = "不允许的http方法";
break;
case 409:
msg = "资源冲突,或者资源被锁定";
break;
case 415:
msg = "不支持的数据(媒体)类型";
break;
case 429:
msg = "请求过多被限制";
break;
case 500:
msg = "系统内部错误";
break;
case 502:
msg = "网络异常";
break;
default:
break;
}
if (onRejected) return onRejected(error, msg);
if (msg) {
MessagePlugin.error(msg);
}
return Promise.reject(error);
}
);
if (reqInterceptor) {
instance.interceptors.request.use(reqInterceptor);
}
instance.interceptors.request.use(
(config) => {
// 是否需要设置 token
config.headers.Authorization = `Bearer ${getAccesstoken()}`;
config.headers["Accept-Language"] = getServerLang();
config.headers["Content-Type"] = config.headers["Content-Type"]
? config.headers["Content-Type"]
: "application/json";
// 签名相关
// step1 获取URLI
const url = config.url;
// body
const body = config.data;
// openid 没有必须给空字符串
const openid = getOpenid() || "";
// 生成签名
const hmac = CryptoJS.HmacSHA256(
`${url}${random}${teamId}${timestamp}${JSON.stringify(body)}${openid}`,
openid
);
const sign = CryptoJS.enc.Base64.stringify(hmac);
config.headers.Nonce = random;
return config;
},
(error) => {
Promise.reject(error);
}
);
return instance;
}
这段代码是一个用于创建 Axios 实例的函数,函数的作用是根据传入的参数配置一个特定的 Axios 实例并返回。
函数的参数包括:
baseUrl
:请求的基础 URL,用于指定请求的目标地址。reqInterceptor
:请求拦截器函数,用于在发送请求前对请求进行处理。onRejected
:错误处理函数,用于在请求发生错误时进行处理。
函数内部的逻辑包括:
- 创建一个 Axios 实例,并设置基础配置项,包括 baseURL 和 timeout。
- 添加响应拦截器,用于处理请求结果。
- 在响应拦截器中,根据不同的 HTTP 响应状态码,进行相应的处理,如显示错误信息、刷新 token、跳转页面等。
- 如果存在请求拦截器函数
reqInterceptor
,则添加请求拦截器。 - 在请求拦截器中,根据需要设置请求的头部信息,如授权信息、语言、签名等。
- 返回配置好的 Axios 实例。
可以使用这个函数来创建一个定制化的 Axios 实例,并根据业务需求进行相应的配置和处理。这样的方式可以提高代码的复用性和可维护性,使得在整个项目中可以方便地使用这个定制化的 Axios 实例来发送请求和处理响应
举例使用
js
function getApiConfig(env: string): ApiConfig {
if (env === "PRE") {
return {
};
}
if (env === "PROD") {
return {
};
}
// 默认开发环境
return {
};
}
这段代码是一个根据环境变量返回对应的 API 配置的函数。
函数的参数是一个字符串 env
,表示当前的环境。
函数内部的逻辑包括:
- 如果环境变量
env
的值为 "PRE",则返回一个预发布环境的 API 配置对象。 - 如果环境变量
env
的值为 "PROD",则返回一个生产环境的 API 配置对象。 - 如果环境变量
env
的值不是 "PRE" 或 "PROD",则返回一个默认的开发环境的 API 配置对象。
返回的 API 配置对象是一个空对象 {},在实际应用中,可以根据具体的需求为配置对象添加相应的属性和值,例如设置不同环境下的接口地址、请求头部信息、超时时间等。
使用这个函数可以根据运行环境动态地获取到相应的 API 配置,使得不同环境下的 API 信息可以分开管理,提高代码的可维护性和灵活性
我们可以通过如下方式,传入自己定义错误异常方法,并设置回调。
js
export const lssClientOrganizeProjectRequest = createApiInstance(
getApiConfig(Env)["client-organize"],
(config) => {
config.headers.teamId = getProjectTeamID();
return useProjectId(config);
},
onHandleError
);
其中
js
export const onHandleError = (error, commonErrMsg?: string) => {
const { data }: any = error.response;
return Promise.reject(new Error(commonErrMsg || data?.message));
};
总结
封装全局方法可以提供以下好处:
- 代码复用:将公共的请求逻辑封装在全局方法中,可以在不同的模块和组件中重复使用,减少代码重复。
- 统一管理配置:在全局方法中配置请求的基本信息,如请求的域名、超时时间、请求头设置等,便于管理和维护。
- 统一异常处理:自定义异常处理方法可以捕获请求过程中出现的异常,并进行统一的处理,例如显示错误信息、重定向页面等。
- 提供灵活性:全局方法可以根据具体需求进行定制,可以在请求过程中加入自定义的拦截器,进行请求的预处理或后处理。
自定义异常处理方法的意义:
- 统一错误信息显示:自定义异常处理方法可以在发生请求错误时进行统一的处理,例如弹出错误提示框或在页面上显示错误信息,提升用户体验。
- 错误处理逻辑:可以根据不同的错误类型,提供特定的处理逻辑,例如根据错误状态码进行重定向、刷新 Token 等操作。
- 更好的错误追踪和记录:自定义异常处理方法可以将异常信息记录下来,方便后续的调试和错误追踪。
设置请求头参数的意义:
- 身份认证:可以在请求头中设置用于身份认证的信息,例如用户的身份凭证或令牌。
- 数据格式指定:可以在请求头中设置请求的数据格式,例如 Content-Type,指定请求体中的数据格式。
- 语言指定:可以在请求头中设置语言参数,用于多语言环境下的国际化支持。
- 版本控制:可以在请求头中设置版本号等参数,方便控制和管理 API 的版本。
通过封装全局方法、自定义异常处理和设置请求头参数,可以提高代码的可维护性、可扩展性和错误处理的统一性,简化开发流程,提升开发效率