背景
在大型项目中,大量的请求,大量重复的代码,让请求封装成为一种必然。
步骤
想要的接口使用方式:
async function getData(params){
return await requestGet(api,params)
}
getData({id:1}).then(res=>{
// 得到接口数据res即data
})
// 或者
let res = await getData({id:1})
requestGet就是要实现的接口封装。
要做的事情
- 封装fetch方法
- 在请求之前携带headers、content-type、cookie,get请求将参数带在url后面,post请求将参数携带在body参数
- 请求过程当中,注意网络错误捕获并抛出
- 请求正常时,将数据返回给开发者,异常时,统一捕获错误,并抛出
代码实现
import { addDomain } from '@/env';
import onion from './onion.js';
import { message } from 'antd';
/*
** 兼容两种情况
** 1.帮助用户处理错误信息,用户得到的直接就是data数据
** 2.用户自己处理错误信息,我们把请求所有res返回
*/
function isReturnAllData(obj, res) {
const { OPTIONS } = obj;
if (OPTIONS && OPTIONS.getAll) {
return true;
} else {
if (res.msg && res.ret !== '0') {
message.error(res.msg);
}
return false;
}
}
export async function request(obj) {
let res = await onion(obj);
return isReturnAllData(obj, res) ? res : res.data;
}
export async function requestGetI(url, params, OPTIONS) {
url = addDomain(url);
return await request({ url, options: { method: 'get', params }, OPTIONS });
}
export async function requestPostI(url, params, OPTIONS) {
url = addDomain(url);
return await request({ url, options: { method: 'post', body: params }, OPTIONS });
}
// onion.js
import { formatQueryObj } from '@/common/utils';
import { message } from 'antd';
// 用reduce实现promise的串行
export default function onion(obj) {
return [frontInterCept, fetcher, endInterCept].reduce((prev, curr) => {
return prev
.then((res) => curr(res))
.catch((err) => {
message.error(err);
});
}, Promise.resolve(obj));
}
// 前置拦截
function frontInterCept(obj) {
return new Promise((resolve, reject) => {
let optionFinal = {};
const { url, options } = obj;
const { method, params, body } = options;
let headers = {};
if (body) {
if (body instanceof window.FormData) {
/* 单独处理请求参数中存在二进制文件的情况
** 此时不需要加content-type
** 浏览器发现是二进制文件,会自动加content-type和boundary
** 手动加content-type的话 boundary中的内容会丢失
*/
} else {
// 普通post请求
headers = { 'content-type': 'application/json; charset=utf-8' };
}
} else {
// get请求
headers = { 'content-type': 'application/x-www-form-urlencoded; charset=utf-8' };
}
optionFinal = method === 'post' ? { headers, method, body: JSON.stringify(body) } : { method, params };
// 允许跨域携带cookie
optionFinal.credentials = 'include';
// 允许跨域请求
optionFinal.mode = 'cors';
const tmpQuery = formatQueryObj(params);
let urlI = tmpQuery ? url + (url.indexOf('?') !== -1 ? '&' : '?' + tmpQuery) : url;
return resolve({ url: urlI, optionFinal });
});
}
// 后置拦截
function endInterCept(obj = {}) {
const { ret, data } = obj;
return new Promise((resolve, reject) => {
switch (ret) {
case '3': {
// 跳转登录页面
return reject('未登录');
}
default: {
/*
** 单独处理登录,未登录直接跳转
** 其他错误,将接口所有数据返回,在request那边统一处理
*/
return resolve(obj);
}
}
});
}
async function fetcher(obj = {}) {
return new Promise((resolve, reject) => {
const { url, optionFinal } = obj;
window
.fetch(url, optionFinal)
.then((res) => {
if (res.status !== 200) {
return reject('网络错误,请稍后重试~');
} else {
// fetch返回的是一个流对象
res
.json()
.then((res) => {
console.log('res', res);
return resolve(res);
})
.catch((err) => {
return reject('网络错误,请稍后重试~');
});
}
})
.catch((err) => {
return reject('网络错误,请稍后重试~');
});
});
}
效果:
兼容处理开发者是否需要自己处理错误信息的情况,默认帮他处理