现在,在项目中,广泛采用Axios库进行Http接口请求。它是基于promise的http库,可运行在浏览器端和node.js中。此外还有拦截请求和响应、转换JSON数据、客户端防护XSRF等优秀的特性。axios源码javascript
「连接-axios中文文档」vue
考虑到各个项目实际使用时写法混乱,不统一。对Axios进行一下通用化的封装,目的是帮助简化代码和利于后期的更新维护,尽可能通用化。java
项目代码自己依附于@vue/CLI3构建,固然经过剥离vue代码,该Axios封装也能够直接应用到其余项目中。node
封装要达成的目标:webpack
npm install axios;
复制代码
经过建立实例,操做实例的方式进行接口请求。ios
//request.js
import axios from 'axios'; // 引入axios
import Qs from 'qs'; // 引入qs模块,用来序列化post类型的数据
import { autoMatch, checkStatus } from './utils'; // 处理函数
import { Toast } from 'mint-ui'; //提示框
// 建立axios实例
const instance = axios.create({
// baseURL: process.env.BASE_URL,
timeout: 30000, // 请求超时时间
// `transformRequest` 容许在向服务器发送前,修改请求数据
transformRequest: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,容许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return JSON.parse(data);
}]
})
复制代码
给实例添加请求和响应拦截器,根据实际数据格式进行相应的处理。
好比:
请求时,应后端要求根据Content-type设置data传参格式;
响应时,统一处理登陆超时的状况和请求失败的错误提示处理等。git
//request.js
// 实例添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求以前作处理...
config.headers = Object.assign(config.method === 'get' ? {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=UTF-8'
} : {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}, config.headers);
if (config.method === 'post') {
const contentType = config.headers['Content-Type'];
// 根据Content-Type转换data格式
if (contentType) {
if (contentType.includes('multipart')) { // 类型 'multipart/form-data;'
// config.data = data;
} else if (contentType.includes('json')) { // 类型 'application/json;'
// 服务器收到的raw body(原始数据) "{name:"nowThen",age:"18"}"(普通字符串)
config.data = JSON.stringify(config.data);
} else { // 类型 'application/x-www-form-urlencoded;'
// 服务器收到的raw body(原始数据) name=nowThen&age=18
config.data = Qs.stringify(config.data);
}
}
}
return Promise.resolve(config);
}, function (error) {
// 对请求错误作处理...
return Promise.reject(error);
});
// 实例添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据作处理,如下根据实际数据结构改动!!...
// const { reason_code } = response.data || {};
// if (reason_code === '001') { // 请求超时,跳转登陆页
// const instance = Toast('请求超时,即将跳转到登陆页面...');
// setTimeout(() => {
// instance.close();
// window.location.href = '/login';
// }, 3000)
// }
return Promise.resolve(checkStatus(response));
}, function (error) {
// 对响应错误作处理...
return Promise.reject(checkStatus(error.response));
});
复制代码
处理错误状态:github
//utils.js
export function checkStatus (response) {
const status = response.status || -1000; // -1000 本身定义,链接错误的status
if ((status >= 200 && status < 300) || status === 304) {
// 若是http状态码正常,则直接返回数据
return response.data;
} else {
let errorInfo = '';
switch (status) {
case -1:
errorInfo = '远程服务响应失败,请稍后重试';
break;
case 400:
errorInfo = '400:错误请求';
break;
case 401:
errorInfo = '401:访问令牌无效或已过时';
break;
case 403:
errorInfo = '403:拒绝访问';
break;
case 404:
errorInfo = '404:资源不存在';
break;
case 405:
errorInfo = '405:请求方法未容许'
break;
case 408:
errorInfo = '408:请求超时'
break;
case 500:
errorInfo = '500:访问服务失败';
break;
case 501:
errorInfo = '501:未实现';
break;
case 502:
errorInfo = '502:无效网关';
break;
case 503:
errorInfo = '503:服务不可用'
break;
default:
errorInfo = `链接错误`
}
return {
status,
msg: errorInfo
}
}
}
复制代码
实例封装到async\await异步函数中。web
//request.js
const request = async function (opt) {
try {
const options = Object.assign({
method: 'get',
ifHandleError: true // 是否统一处理接口失败(提示)
}, opt);
// 匹配接口前缀 开发环境则经过proxy配置转发请求; 生产环境根据实际配置
options.baseURL = autoMatch(options.prefix);
const res = await instance(options);
// console.log(res);
if (!opt.ifHandleError) { // 自定义参数,是否容许全局提示错误信息
Toast(res.error || '请求处理失败!')
}
return res;
} catch (err) {
if (!opt.ifHandleError) { // 自定义参数,是否容许全局提示错误信息
Toast(err.msg || '请求处理失败!')
}
return err;
}
}
复制代码
项目中统一处理报错提示。但有时对于接口请求比较多的状况,不少时候并不但愿某些接口被全局报错提示。这时这种接口能够设置自定义的ifHandleError参数处理,不进行全局错误提示,而是到业务代码中再作处理。
同理,其余的一些特殊业务状况也能够经过自定义参数处理。shell
对于项目中请求多个后端的接口时,每一个接口在请求封装时附带prefix参数,根据prefix的值autoMatch函数
统一处理,自动匹配接口前缀。
autoMath方法:
// utils.js
const isDev = process.env.NODE_ENV === 'development'; // 开发 or 生产
// 匹配接口前缀
export function autoMatch (prefix) {
let baseUrl = '';
if (isDev) {
// 开发环境 经过proxy配置转发请求;
baseUrl = `/${prefix || 'default'}`;
} else {
// 生产环境 根据实际配置 根据 prefix 匹配url;
// 配置来源 根据实际应用场景更改配置。(1.从全局读取;2.线上配置中心读取)
switch (prefix) {
case 'baidu':
baseUrl = window.LOCAL_CONFIG.baidu;
break;
case 'alipay':
baseUrl = window.LOCAL_CONFIG.alipay;
break;
default:
baseUrl = window.LOCAL_CONFIG.default;
}
}
return baseUrl;
}
复制代码
webpack配置,好比:@vue/CLI3在vue.config.js
中配置:
devServer: {
proxy: {
'/baidu': {
target: 'http://10.59.81.31:8088',
changeOrigin: true,
pathRewrite: { '^/baidu': '' }
},
'/default': {
target: 'http://10.59.81.31:8088',
changeOrigin: true,
pathRewrite: {'^/default' : ''}
},
}
},
复制代码
在一个新文件apiUrl.js
中统一管理项目所有的接口,方便维护。
//apiUrl.js
export const apiUrl = {
login: '/api/login',
loginOut: '/api/loginOut',
qryPageConfig: '/test/qryPageConfig',
setPageConfig: '/test/setPageConfig',
//...
}
复制代码
在index.js
中:
import Vue from 'vue';
import request from './request';
import { apiUrl } from './apiUrl';
let services = {};
Object.entries(apiUrl).forEach((item) => {
services[item[0]] = function (options = {}) {
return request(Object.assign({
url: item[1]
}, options))
}
})
// 将services挂载到vue的原型上
// 业务中引用的方法:this.$services.接口名(小驼峰)
Object.defineProperty(Vue.prototype, '$services', {
value: services
});
export default services;
复制代码
在上述代码中,经过读取配置的接口信息(名称和请求路径),生成所有接口。 并挂载到vue的原型上,这样在业务中就能够经过this.$services.接口名
直接调用对应的接口。
固然,这是本项目使用的方法。 因具体项目而异,也能够不挂载到Vue原型上,使用services模块;或直接调用request.js
封装的请求函数。
如下为使用事例:
// index.vue methods中
methods: {
// 获取页面配置信息接口 get Promise
$_qryPageConfig() {
this.$services.qryPageConfig({
method: 'get', //默认
params: {
page: 'login'
},
}).then((res) => {
this.pageConfig = res.data;
}).finally(() => {
...
this.close();
});
},
// 设置页面配置接口 post async/await
$_order: async function () {
this.loading = true;
const res = await this.$services.setPageConfig({
prefix: 'baidu', //匹配url前缀
ifHandleError: true, //不对该接口进行全局错误提示。
method: 'post',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
data: {
userId: this.idCard,
userName: this.realName,
color: 'red',
}
})
this.loading = false;
if (res.data) {
Total('success');
}
},
}
复制代码
项目代码自己依附于@vue/CLI3构建,是CLI3构建移动H5端应用的一部分。固然经过剥离vue代码,该Axios封装也能够直接应用到其余项目中。
项目源码:
@vue/CLI3构建移动H5端应用:juejin.im/post/5d674d…;
Github连接:github.com/now1then/vu…;