什么决定了一个项目架构设计的好坏?很多优秀的项目构架都是基于分层的思想,比如开放互联的网络协议,分层方便我们快速调整以及功能的丰富。在前端项目中,我们也会做一些通用的逻辑设计,比如接口请求层、路由层、权限处理等等,这些是支撑一个复杂前端项目的基础。本文我对接口请求层做一个封装,查看示例代码。
1. 功能设计
- 支持取消请求 (这里要兼顾多种场景)
- 路由跳转时,将未完成的请求取消,只取消 GET 请求;
- 将两个相同的请求的前者取消,如何判断相同?url相同以及请求方法相同,只取消 GET 请求;
- 开放取消某一个请求的能力,方便下一层业务逻辑的处理
- 超时时间设置
- 登录状态异常处理
- 是否全局异常
- 是否全局 loading
- 错误异常是否 抛出 或者 return
- 支持最小响应时间设置
- 请求超时的重发
2. 代码实现
import axios, { AxiosRequestConfig } from "axios";
import UrlParse from "url-parse";
import { getToken } from "@/utils/auth";
import { Message } from "element-ui";
const cancelTokens: Function[] = [];
export function clearRequest() {
while (cancelTokens.length) {
const fn = cancelTokens.shift() as Function;
fn("user-cancel");
}
}
class AuthError extends Error {
constructor(message: string) {
super(message);
this.name = "auth-error";
}
}
const baseURL = "http://renwu.airtlab.com/api/";
const service = axios.create({
baseURL,
timeout: 10000
});
service.interceptors.request.use(
config => {
config.headers["token"] = getToken();
return config;
},
error => {
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
const data = response.data;
// 自定义异常逻辑
data.success = data.retCode === "0";
if (!data.success && !(data instanceof Blob)) {
return Promise.reject(new Error(data.msg || `未知错误 data (${data}) `));
}
return data;
},
error => {
if (error.message.includes("401")) {
return Promise.reject(new AuthError("未登录"));
} else {
return Promise.reject(error);
}
}
);
interface RequestOption extends AxiosRequestConfig {
minTime?: number; // 最小请求时间
gLoading?: boolean; // 全局loading
igError?: boolean; // 全局错误提示
query?: object; // 查询字符串
cancelResolver?: (cancelFn: Function) => void;
}
export function createRequest(option: RequestOption) {
return function (params: RequestOption) {
return request({ ...option, ...params })
}
}
export default function request(option: RequestOption, count = 0) {
if (option.timeout === 0) {
option.timeout = 1000 * 3600 * 24; // 不设置超时时间,24h
}
if (!option.minTime) {
option.minTime = 200;
}
if (option.query) {
const o = new UrlParse(baseURL + option.url, true);
Object.assign(o.query, option.query);
option.url = o.toString();
delete option.query;
}
const cancelToken = new axios.CancelToken(function executor(fn) {
if (option.cancelResolver) {
option.cancelResolver(() => {
fn('user-cancel');
});
} else {
cancelTokens.push(fn);
}
})
const p1 = service({ ...option, cancelToken });
const p2 = new Promise(resolve => setTimeout(() => resolve(1), option.minTime));
return Promise.allSettled([p1, p2]).then((res: any) => {
const { value, reason } = res[0];
if (!reason) return value;
const isTimeout = reason.message.indexOf("timeout") === 0;
const message = isTimeout ? "接口响应超时" : reason.message;
if (isTimeout) {
if (count === 0) {
return request(option, 1)
}
}
if (reason instanceof axios.Cancel) {
// user-cancel
option.igError = true;
return Promise.reject(reason);
}
if (reason instanceof AuthError) {
Message("未登录,去登录");
return;
}
if (!option.igError) {
Message({
type: "error",
message: message
});
return Promise.reject(reason);
}
});
}
网友评论