目录
转载
愣锤 写的确实不错,我收益匪浅,其中我也加入了自己的一些见解。
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
特性
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
安装
npm install axios
引入
一般我会在项目的src目录中,新建一个utils文件夹,然后在里面新建一个http.js用来封装axios。
/**
* axios封装
* 请求拦截、响应拦截、错误统一处理
*/
import axios from 'axios';
/**
* qs.parse()是将URL解析成对象的形式
* qs.stringify()是将对象 序列化成URL的形式,以&进行拼接
*/
import qs from 'qs'; // 根据需求是否导入qs模块
环境切换
我们的项目环境可能有开发环境、测试环境和生产环境。我们通过node的环境变量来匹配我们的默认的接口url前缀。axios.defaults.baseURL可以设置axios的默认公共请求地址。
// 环境的切换
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'http://*****.com/';
}
设置请求超时
通过axios.defaults.timeout设置默认的请求超时时间。例如超过了10s,就会告知用户当前请求超时,请刷新等。
axios.defaults.timeout = 10000;
post请求头的设置
post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置。
instance.defaults.headers.post['Content-Type'] = 'application/json';
- 请求拦截
我们在发送请求前可以进行一个请求的拦截,为什么要拦截呢,我们拦截请求是用来做什么的呢?比如,有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。
请求拦截
/**
* 请求拦截器
* 每次请求前,如果存在token则在请求头中携带token
*/
axios.interceptors.request.use(
config => {
// 登录流程控制中,根据本地是否存在token判断用户的登录情况
// 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
// 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
// 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
const token = store.state.token;
token && (config.headers.Authorization = token);
return config;
},
error => Promise.error(error))
这里说一下token,一般是在登录完成之后,将用户的token通过localStorage或者cookie存在本地,然后用户每次在进入页面的时候(即在main.js中),会首先从本地存储中读取token,如果token存在说明用户已经登陆过,则更新vuex中的token状态。然后,在每次请求接口的时候,都会在请求的header中携带token,后台人员就可以根据你携带的token来判断你的登录是否过期,如果没有携带,则说明没有登录过。这时候或许有些小伙伴会有疑问了,就是每个请求都携带token,那么要是一个页面不需要用户登录就可以访问的怎么办呢?其实,你前端的请求可以携带token,但是后台可以选择不接收啊!
响应拦截
// 响应拦截器
axios.interceptors.response.use(
// 请求成功
res => res.status === 200 ? Promise.resolve(res) : errorHandle(res.status, res.statusText),
// 请求失败
error => {
const { response } = error;
if (response) {
// 请求已发出,但是不在2xx的范围
errorHandle(response.status, response.statusText);
return Promise.reject(response);
} else {
// 处理断网的情况
// eg:请求超时或断网时,更新state的network状态
// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// 关于断网组件中的刷新重新获取数据,会在断网组件中说明
if (!window.navigator.onLine) {
tip("网络有些波动,请稍后");
} else {
return Promise.reject(error);
}
}
});
响应拦截器很好理解,就是服务器返回给我们的数据,我们在拿到之前可以对他进行一些处理。例如上面的思想:如果后台返回的状态码是200,则正常返回数据,否则的根据错误的状态码类型进行一些我们需要的错误,其实这里主要就是进行了错误的统一处理和没登录或登录过期后调整登录页的一个操作。
错误处理封装
/**
* 请求失败后的错误统一处理 请求成功除200外的处理
* @param {Number} status 请求失败的状态码
* @param {String} other 请求失败的描述
*/
const errorHandle = (status, other) => {
// 状态码判断
switch (status) {
// 401: 未登录状态,跳转登录页
case 401:
toLogin();
break;
// 403 token过期
// 清除token并跳转登录页
case 403:
tip('登录过期,请重新登录');
localStorage.removeItem('token');
setTimeout(() => {
toLogin();
}, 1000);
break;
// 404请求不存在
case 404:
tip('请求的资源不存在');
break;
default:
tip(other);
}
}
主要处理请求成功后除200外的状态和请求失败的状态,因为本身返回的就有statusText,所以这块主要是自定义一些自己的错误描述或者错误弹窗等。
错误描述封装
由于全局引入的ElementUI,所以在js文件内引用需要创建vue的实例。
// js文件内引用ElementUI组件
//1.引入vue
import Vue from 'vue';
//2.新创建一个vue实例
let v = new Vue();
/**
* 提示函数
* 禁止点击蒙层、显示一秒后关闭
*/
const tip = msg => {
v.$message({
showClose: true,
message: msg,
type: 'error',
duration: 3000
})
}
跳转登录页封装
import router from '../router';
/**
* 跳转登录页
* 携带当前页面路由,以期在登录页面完成登录后返回当前页面
* this.$router.replace()跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。
*/
const toLogin = () => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}
封装GET方法和POST方法
get方法:get函数有三个参数,第一个参数表示我们要请求的url地址,第二个参数是我们要携带的请求参数,第三个参数是我们处理数据的回调函数。
post方法:post方法同理个体方法,只是多了一个对象类型的paramsUrl(地址上的)参数,需要拼接在url地址上。
export default {
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
* @param {Function} callback [回调函数]
*/
get(url, params, callback) {
return axios.get(url, {
params: params
})
.then(res => {
callback(res.data);
})
},
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} paramsUrl [url携带的参数]
* @param {Object} params [请求时携带的参数]
* @param {Function} callback [回调函数]
*/
post(url, paramsUrl, params, callback) {
return axios.post(createURL(url, paramsUrl), qs.stringify(params))
.then(res => {
callback(res.data);
})
}
}
对象拼接在url上
需要判断url本身是否存在"?",来判断第一个"&"是否显示,最后把可能出现的空格去掉。
/**
* 对象拼接在url上
* @param {String} url [请求的url地址]
* @param {Object} param [url携带的参数]
*/
const createURL = (url, param) => {
var urlLink = '';
for (key in param) {
urlLink += '&' + key + '=' + param[key];
}
var flag = url.index("?") > -1;
urlLink = url + flag ? "" : "?" + flag ? urlLink : urlLink.substr(1);
return urlLink.replace(' ', '');
}
axios的使用
组件内methods定义方法,在created调用方法
created() {
// 调用获取数据列表方法
this.getList();
},
methods: {
// 获取数据列表
getList() {
this.$http.get(`/list.json`, {}, function(res) {
console.log(res);
});
}
}
在main.js全局定义了http,在组件内直接this.$http.get()、this.$http.post()来调用。
import http from './utils/http';
Vue.prototype.$http = http;