携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
一、需求场景
1.axios是基于promise的网络请求库,封装了请求方法。
2.在一些比较大型的项目中,请求接口数量众多,我们会把接口抽取出来或者按模块管理。
3.接口封装,调用简易为上。
二、header头Content-Type信息
在此对header头的信息不作赘述,不过在日常工作中,遇到http错误400,一般可以从前后端数据格式(入参和出参)是否不一致、数据是位于请求体或是请求头中、token的位置等角度排错。 Content-Type用于描述传输的数据的类型,告诉对应的客户端如何解码。 本文重点介绍以下几个与工作相关的属性
1.application/x-www-form-urlencoded
默认的post请求的提交方式,请求的数据会以键值对的形式拼接(key1=val1&key2=val2) 注意:get请求没有请求体,因此content-type设置了没有效果,且get请求的编码方式是各浏览器决定的
2.mulitPart/form-data 表单方式提交;最常见的的post的提交方式
3.application/json 序列化的JSON串
总结:
遇到要传JSON值,需要手动设置content-type :application/json
遇到需要传递From URL Encoded 格式的formData,需要手动设置 content-type:application/x-www-form-urlencoded,并且使用 qs.stringify (data) 将data转换为url格式,才能正常使用,或者将data转换为formData格式
遇到需要文件流 (Multipart From)格式的formData,需要 手动构建formData 数据,(new formData.append('a',1),...), 然后去掉所有的 content-type设置。也就是不对content-type进行设置。
介绍:qs序列化,将对象序列化为字符串
let data = {
orgId: 1,
systemName: ' 机构',
};
qs.stringify(data); //'orgId=1&ststemName=机构'
上述内容了解即可,不必深究,只要知道参数是什么格式、参数放在什么位置即可。
三、实践
1.get、post请求统一处理
前端请求参数都为对象,因此在get请求中表现为把对象拼接在url后面,post请求则是将请求对象转换成formdata对象,以mulitPart/form-data格式传输
function getParamSpell(url, param) {
let paramString = [];
for (let key in param) {
if (param[key] !== undefined && param[key] !== null && param[key] !== '') {
paramString.push(key + "=" + param[key])
}
}
if (paramString.length > 0) {
url = url + "?" + paramString.join("&")
}
return url
}
function dataFromTransfer(param) {
let fromParam = new FormData();
for (let key in param) {
// 判断类型 '[object Object]' 代表对象 '[object Array]'代表数组
if (param[key] !== undefined && param[key] !== null) {
if (Object.prototype.toString.call(param[key]) === '[object Object]' || Object.prototype.toString.call(param[key]) === '[object Array]') {
fromParam.append(key, JSON.stringify(param[key]));
} else {
fromParam.append(key, param[key]);
}
}
}
return fromParam;
}
get请求
get(url, param, success, error, mockConfig) {
this.getBase(url, param, {}, success, error, mockConfig);
}
// 将请求参数对象格式化为 key=val&&key2=val2的格式
getBase(url, param, config, success, error, mockConfig) {
url = getParamSpell(url, param);
this.generateMock(url, 'GET', mockConfig)
axios.get(url, config).then(response => {
checkResult(response, success, error);
}).catch((fault) => {
errorResult(fault, error)
})
},
}
post请求
post(url, param, success, error, mockConfig) {
this.postBase(url, param, {}, success, error, mockConfig)
},
postBase(url, param, config, success, error, mockConfig) {
this.generateMock(url, 'POST', mockConfig)
axios.post(url, dataFromTransfer(param), config).then(response => {
checkResult(response, success, error);
}).catch((fault) => {
errorResult(fault, error)
});
},
patch、put等请求不做其他特殊处理:
patch(url, param, success, error, mockConfig) {
this.generateMock(url, 'PATCH', mockConfig)
axios.patch(url, dataFromTransfer(param)).then(response => {
checkResult(response, success, error);
}).catch((fault) => {
errorResult(fault, error)
})
},
请求成功和失败的处理:
这里除了要判断网络请求状态,也要判断具体的业务状态码,具体情况根据业务决定
function errorResult(fault, error) {
if (logError) {
console.log("http error is :", fault)
}
if (error) {
error('操作失败');
}
}
//跳转登录
function goToPath() {
let fullPath = window.document.location.href;
let filePath = window.document.location.pathname;
let pos = fullPath.indexOf(filePath);
let host = fullPath.substring(0, pos);
if (process.env.NODE_ENV === 'production') {
// 正式环境跳转至主域名
window.location.href = `${host}`
} else {
window.location.href = "http://www.baidu.com"
}
}
function checkResult(response, success, error) {
let statusCode = response.status;
let responseData = response.data;
//请求不成功
if (200 !== statusCode) {
if (error) {
error(statusCode);
}
return
}
let responseCode = responseData.code
//请求成功,请求数据正确
if (0 === responseCode) {
if (typeof success === 'function') {
if (success) {
success(responseData.data);
}
}
return
}
//请求成功,文件类型请求
if (typeof success === 'object' && 'file' === success.type) {
if (success.callback) {
success.callback(response);
}
return
}
//请求成功,请求有错误
if (error) {
error(responseData.msg, responseData.code);
if (responseData.msg == '登录过期,请重新登录') {
goToPath()
}
}
}
文件类型的请求:
uploadFile(url, param, success, error, mockConfig) {
this.postBase(url, param, {
headers: {
"Content-type": "multipart/form-data",
"Access-Control-Allow-Origin": "*",
}
}, success, error, mockConfig)
},
downloadFile(url, param, success, error, fileName, mockConfig) {
this.getBase(url, param, {
responseType: 'blob',
}, {
type: 'file',
callback: result => {
if (success) {
success(true)
}
let url = window.URL.createObjectURL(result.data);
let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}, error, mockConfig);
},
其余配置信息:
// 是否输出错误信息
set loggerStatus(status) {
logger = status
},
// 请求的基准地址
set apiHost(apiHost) {
axios.defaults.baseURL = apiHost;
},
get apiHost() {
return axios.defaults.baseURL;
},
set mockEnable(enable) {
if (enable) {
Mock = require('mockjs');
}
},
// 设置请求头,一般用于携带token信息
setCommonHeader(key, value) {
if (!value.isNullOrEmpty()) {
axios.defaults.headers.common[key] = value;
} else {
delete axios.defaults.headers.common[key];
}
},
set timeout(timeout) {
axios.defaults.timeout = timeout;
},
set mockTimeout(timeout) {
if (Mock) {
Mock.setup({
timeout: timeout
})
}
},
调用:
api地址:
www.tianqiapi.com/api?version…
getWeatherTest(params, success) {
http.get("/api", params,
result => {
if (success) {
success(result)
}
}, error => {
if (success) {
success(error)
}
});
}
————————————————————————————————————————————————————————————————————
http.logErrorStatus = true;
http.apiHost = "http://www.tianqiapi.com";
let data = {
version: "v9",
appid: "23035354",
appsecret: "8YvlPNrz",
};
api.testA.getWeatherTest(data, (res) => {
console.log(res);
},(error)=>{
toast.error(error)
}
);
输出: