当业务需求增加时可能会持续更新
目录结构
-- api
-- customFetch.js // 因目前 fetch 无timeout功能,扩展 fetch 函数令其支持timeout
-- index.js // 总接口文件
-- request.js // 实际 request 类
customFetch.js
// 原有fetch方法
const oldFetchfn = fetch;
// 扩展原有fetch方法
export default function (input, opts) {
const fetchPromise = oldFetchfn(input, opts);
// 设置定时器,如果定时器先抛出reject则表示fetch已超时
const timeoutPromise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject(new Error("request timeout"))
}, opts.timeout || 20000)
});
//哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
return Promise.race([fetchPromise, timeoutPromise]);
}
request.js
import { cloneDeep } from 'lodash'
import { message } from 'ant-design-vue';
import customFetch from './customFetch';
export default class Request {
constructor(url, data, methods, options) {
this.create(url, data, methods, options);
}
url = "";
initObject = {};
/**
* @param showAlert // 是否展示错误提示弹窗
* @param responseType
* @param timeout
* @param redirectToLogin // 是否在登录态失效时跳转login页面(未实现,预留)
*/
options = {
showAlert: true, // 弹出message错误提示
responseType: 'json', // 'json' 'blob' 'arraybuffer' 'text'
};
// 将 object 转为 querystring 键值对形式
// e.g: { property1: 123 } -> property1=123
stringifyQueryString(obj, arr = [], idx = 0) {
const arrCopy = cloneDeep(arr);
for(let item in obj){
arrCopy[idx++] = [item, obj[item]];
}
return new URLSearchParams(arrCopy).toString();
}
// 构造fetch第二个入参 initObject
createInitObject(data, method, options) {
const headers = new Headers({
"Content-Type": "application/json"
});
let initOptions = {
method,
credentials: 'same-origin',
headers
}
if(options.timeout){
initOptions = Object.assign(initOptions, {
timeout: options.timeout,
})
}
if(!['get', 'head'].includes(method.toLowerCase())){
initOptions = Object.assign(initOptions, {
body: JSON.stringify(data),
})
}
return initOptions;
}
// 初始化(载入url, 配置initObject,配置options)
create(url, data, method, options) {
let urlOption = url;
if(['get', 'head'].includes(method.toLowerCase())){
urlOption = `${url}${data ? '?' : ''}${this.stringifyQueryString(data)}`;
}
const initObject = this.createInitObject(data, method, options);
this.url = urlOption;
this.initObject = initObject;
if(options && Object.keys(options).length){
this.options = options;
}
}
// 调用fetch
run() {
return customFetch(this.url, this.initObject).then(res => {
if(!res.ok){
this.options.showAlert && message.error(res.statusText)
return res.json();
}
let result = undefined;
// 在接收文件时,可能会接收到错误提示,如果responseType为“application/json”时则忽视options.responseType强行转为json
if (res.headers.get('content-type').includes('application/json')) {
result = res.json();
} else {
switch(this.options.responseType) {
case 'blob':
result = res.blob();
break;
case 'arraybuffer':
result = res.arrayBuffer();
break;
default:
try{
result = res.json();
} catch (e) {
result = res.text();
}
break;
}
}
return result;
}).catch(err => {
if(err.message === 'request timeout'){
this.options.showAlert && message.error("请求超时");
} else {
this.options.showAlert && message.error("网络错误");
}
return err;
});
}
}
index.js
import Request from "./request";
export default {
test(data, options = {}) {
const url = "/api/planmap/querysswplanningsite";
return new Request(url, data, 'GET', options).run();
}
}
调用
import api from "@/api";
api.test({test: 123}).then(res => {
console.log(res)
});