该文只讲封装,不涉及下载安装原理讲解。以下所有的封装代码需要写在一个文件内并将其暴露,并在写入 API 的文件内引入。
创建http请求
利用axios创建一个实例:
const baseURL = `/${process.env.VUE_APP_API_PREFIX}`; // 接口基本路径
const http = axios.create({
timeout: timeOut,
withCredentials: true,
headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': getToken() },
baseURL
});
withCredentials: true
在发起跨域请i去的时候,后端已经开启CORS,前端需要也携带cookie,此时需要前端的请求头加上此配置,表示请求可以携带cookie。
封装get、post请求
export default{
get(opts) {
return new Promise((resolve, reject) => {
const params = {
method: 'get'
};
http(merge(params, opts))
.then(res => {
// 这里就写业务逻辑请求时拿到的res,一般包含data、code、msg、res等
resolve(res);
})
.catch(err => {
reject(err);
});
});
},
post(opts) {
return new Promise((resolve, reject) => {
const params = {
method: 'post',
headers: {
'Content-Type': 'application/json'
}
};
http(merge(params, opts))
.then(res => {
// 这里就写业务逻辑请求时拿到的res,一般包含data、code、msg、res等
resolve(res);
})
.catch(err => {
reject(err);
});
});
},
}
使用封装好的get、post去请求服务器:
// 普通get请求
function getComponentByIdentification(params) {
return http.get({
url: `/web/system/getComponentByIdentification`,
params
});
}
// get文件下载,此是需要在请求到接口后进行处理blob文件进行创建a标签进行下载
function downloadFile(params) {
return http.get({
url: '/web/protocol/download',
responseType: 'blob',
params
});
}
// 普通post请求
function saveOrUpdateProtocol(params) {
return http.post({
url: 'web/protocolManager/saveOrUpdateProtocol',
data: params
});
}
用法
// get、post请求写法
const param = {
protocolBaseInfo: this.protocolBaseInfo
};
getComponentByIdentification(param)
.then(res => {
// 写业务逻辑
})
// 不传参则直接写
getComponentByIdentification()
.then(res => {
// 写业务逻辑
})
封装download
下载请求,下载请求与get、post不同,需要兼容IE、以及判断下载的是哪种类型的文件。
以下代码对下载excel文件进行了封装,并且对下载失败的情况进行封装。
前端向服务端请求下载文件是以流的格式(stream
)传递到前端,前端通常是将流转换为objectURL
,借用a
标签的download
属性,进行文件下载。
文件下载失败时,服务端消息返回的文件格式不再是流,而是服务端返回的消息json
(errorCode之类)对象,但是依然被包裹再 Blob 类型文件内,此时 blob 的 type 是text/json
,前端虽然可以正常导出文件,但是文件内容是undefined。
值得注意的是
若是下载失败的情况,返回的类型是为
text/json
,但是 IE 和 chrome 两者返回的不完全一样,如下:IE 的 type 为
text/json;charset=utf-8"
chrome 为
text/json
因此 若需要利用返回文件类型为 json 还是 stream,在响应拦截器内对错误状况作出处理时 ,判断文件类型时不能写入
res.type === 'text/json'
,需要写为res.type.indexOf('json') > -1
。
因此需要对文件内容进行解析
download(opts) {
return new Promise((resolve, reject) => {
let newParams = {
method: 'get',
responseType: 'blob'
};
newParams = merge(newParams, opts);
http(newParams)
.then(res => {
const blob = new Blob([res]);
// 判断接收到的文件是否为json类型,是则需要作错误处理
if (res.type.indexOf('json') > -1) {
var newblob = blob.slice(0, blob.size);
// 需要对包裹在blob文件内部的json文件解析出来
var reader = new FileReader();
reader.readAsText(newblob, 'utf-8');
reader.onload = function(evt) {
if (evt.target.readyState === FileReader.DONE) {
const result = JSON.parse(evt.target.result);
// 此时的result 和get、post接口一样返回的json对象内包含data、msg、code等,根据此 可在此处作出一些错误处理(弹出弹框等)
}
};
} else {
// execl文件
const { params } = newParams;
if (params.ext) params.type = params.ext;
if (params.type === 'excel') params.type = 'xlsx';
const fileName = `${params.fileName}.${params.type}`;
if ('download' in document.createElement('a')) {
// 非IE下载
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
} else {
/** IE10+下载**/
navigator.msSaveBlob(blob, fileName);
}
}
resolve(res);
})
.catch(err => {
reject(err);
});
});
}
利用此进行文件下载:
// 文件下载,请求到数据后不需要进行任何处理,由封装好的download处理
function downloadFile(params) {
return http.download({
url: '/web/protocol/download',
params
});
}
上传请求封装
upload(opts) {
opts.url = `${opts.url}.do`;
if (getToken() !== '') {
opts.url += `?_csrf=${getToken()}`;
}
const axiosUpload = axios.create({
baseURL,
timeout: timeOut,
headers: {
'Content-Type': 'multipart/form-data',
'X-Requested-With': 'XMLHttpRequest'
}
});
// 响应拦截器
axiosUpload.interceptors.response.use(function(response) {
// 对错误进行统一处理
return Promise.resolve(response.data);
});
return new Promise((resolve, reject) => {
axiosUpload
.post(opts.url, opts.data)
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
});
});
},
这里的'X-Requested-With': 'XMLHttpRequest'
是让服务端知晓本次请求是ajax请求,看组件业务需求是否需要设置,公司业务是需要和单点登录对接跳转其他链接所用。
利用此进行上传文档
// 文档上传
function uploadFile(file) {
return http.upload({
url: `/web/protocolFile/upload`,
data: file
});
}
// 调用接口时
const form = new FormData();
// 文件对象
form.append('file', file);
// 请求方法
uploadFile(form)
.then(res => {
// 写业务逻辑
})
响应拦截
对于响应拦截,普通的get、post只需要根据错误码弹出错误框即可;但是对于上传、下载接口需要特殊处理。
http.interceptors.response.use(
function(response) {
// 请求文件流 使用get请求下载并且接口返回正常的情况
if (response.data.type === 'application/octet-stream') {
// console.log('文件流');
return response;
}
// 拦截下载接口 如果下载接口使用get请求,需要判断是否为错误的状况
// 错误则需要将blob文件解析,然后赋值给response.data,可以接着走以下的错误处理逻辑
if (response.data.type && response.data.type.indexOf('json') > -1) {
var reader = new FileReader();
reader.addEventListener('loadend', function(e) {
response.data = JSON.parse(e.target.result);
});
reader.readAsText(response.data);
}
// 对错误进行统一处理
if (response.data.code !== '0') {
console.log('cuowule!!');
// 后台返回错误,可配合业务组件的一些信息作一些错误处理
}
// response.data是接口请求完成后.then内拿到的真正的数据,不包含header等
return Promise.resolve(response.data);
},
function(error) {
const response = error.response;
const rp = response.status;
if (rp === 401 || rp === 403 || rp === 302) {
if (process.env.NODE_ENV !== 'development') {
window.location.reload();
}
} else {
if (error.message.indexOf('timeout') > -1) {
// 超时判断,需要弹出超时的弹出框
} else {
// 接口错误,做错误处理
}
}
// 对响应错误做点什么
return Promise.reject(error);
}
);
请求拦截
// 请求拦截器
http.interceptors.request.use(function (config) {
if (config.method === 'get') {
config.url.indexOf('?') > '-1' ?
config.url = config.url + '&_=' + Date.now()
: config.url = config.url + '?_=' + Date.now()
}
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})