模块的选择

Vue中发送网络请求有非常多的方式, 那么, 在开发中, 如何选择呢?

  1. 传统的Ajax是基于XMLHttpRequest(XHR)
  2. 使用jQuery-Ajax
  3. 官方在Vue1.x的时候, 推出了Vue-resource
  4. axios

比较差异: 传统的Ajax配置和调用方式等非常混乱.编码起来看起来就非常蛋疼.所以真实开发中很少直接使用, 而jQuery-Ajax,在Vue的整个开发中都是不需要使用jQuery了.为了方便我们进行一个网络请求, 特意引用一个jQuery,

你觉得合理吗?完全没有必要为了用网络请求就引用这个重量级的框架.Vue作者就在GitHub的Issues中说明了去掉vue-resource, 并且以后也不会再更新.那么意味着以后vue-reource不再支持新的版本时, 也不会再继续更新和维护.
对以后的项目开发和维护都存在很大的隐患.

综上,我们使用axios作为发送网络请求的模块

认识axios

基于promise用于浏览器和node.js的http客户端

功能特点:

  • 支持浏览器和node.js
  • 支持promise
  • 能拦截请求和响应
  • 能转换请求和响应数据
  • 自动转换JSON数据
  • 浏览器端支持防止CSRF(跨站请求伪造)

发送基本请求

HttpBin 介绍

httpbin是一个HTTP Request & Response Service,你可以向他发送请求,然后他会按照指定的规则将你的请求返回。这个类似于echo服务器,但是功能又比它要更强大一些。 httpbin支持HTTP/HTTPS,支持所有的HTTP动词,能模拟302跳转乃至302跳转的次数,还可以返回一个HTML文件或一个XML文件或一个图片文件(还支持指定返回图片的格式)。实在是请求调试中居家必备的良器!

httpbin怎么用

httpbin的使用方法非常简单,你只需要把请求的地址修改为httpbin.org即可。 比如:

1
curl http://httpbin.org/user-agent

查看自己的GET请求

1
curl http://httpbin.org/get

Get request

1
2
3
4
5
6
7
8
9
#Get request
//没有请求参数
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

Post request

1
2
3
4
5
6
7
8
9
10
11
12
#Post request 
//有请求参数
axios.post('/save', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

发送并发请求

有时候, 我们可能需求同时发送两个请求.使用axios.all, 可以放入多个请求的数组.axios.all([])返回的结果是一个数组,使用axios.spread 可将数组 [res1,res2] 展开为 res1, res2

1
2
3
4
5
6
7
8
9
10
11
12
13
function getUserAccount() {
return axios.get('/user/12345');
}

function getUserPermissions() {
return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread((acct, perms)=>{
console.info(acct);
console.info(perms);
}));

可用的api别名

1
2
3
4
5
6
7
8
9
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
#注:当使用以上别名方法时,url,method和data等属性不用在config重复声明。

#全局配置

在上面的示例中, 我们的BaseURL是固定的.事实上, 在开发中可能很多参数都是固定的.这个时候我们可以进行一些抽取, 也可以利用axiox的全局配置

1
2
3
4
5
6
7
8
9
10
11
12
//提取全局配置
axios.defaults.baseURL = ‘http://123.207.32.32:8000’
axios.defaults.headers.post[‘Content-Type’] = ‘application/x-www-form-urlencoded’;

//发送并发请求
axios.all([
axios.get('/user/12345'),
axios.get('/user/12345/permissions')])
.then(axios.spread((acct, perms)=>{
console.info(acct);
console.info(perms);
}));

常见的配置选项

请求地址 url: ‘/user’
请求类型 method: ‘get’,
请根路径 baseURL: ‘http://www.mt.com/api',
请求前的数据处理 transformRequest:[function(data){}],
请求后的数据处理 transformResponse: [function(data){}],
自定义的请求头 headers:{‘x-Requested-With’:’XMLHttpRequest’},
URL查询对象 params:{ id: 12 },
查询对象序列化函数 paramsSerializer: function(params){ }
request body data: { key: ‘aa’},
超时设置s timeout: 1000,
跨域是否带Token withCredentials: false,
自定义请求处理 adapter: function(resolve, reject, config){},
身份验证信息 auth: { uname: ‘’, pwd: ‘12’},
响应的数据格式 json / blob /document /arraybuffer / text / stream responseType: ‘json’,

axios实例

为什么要创建axios的实例呢?

当我们从axios模块中导入对象时, 使用的实例是默认的实例.当给该实例设置一些默认配置时, 这些配置就被固定下来了.但是后续开发中, 某些配置可能会不太一样.比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.

拦截器

axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。

拦截器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 添加一个请求拦截器
axios.interceptors.request.use(function (response) {
console.info("来到了request拦截的success中")
return response;
}, function (error) {
console.info("来到了request拦截的error中")
return error;
});

// 添加一个响应的拦截器
axios.interceptors.response.use(function (response) {
console.info("来到了response拦截的success中")
return response.data;
}, function (error) {
console.info("来到了response拦截的error中")
return error;
});

拦截器中都做什么呢?

请求拦截可以做到的事情:

  1. 当发送网络请求时,在页面中添加一个loading组件,作为动画
  2. 某些请求要求用户必须登入,判断用户是否有token,如果没有token跳到login页面
  3. 对请求参数进行序列化

添加响应拦截器,统一处理服务器响应和异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 添加响应拦截器,统一处理服务器响应和异常
*/
axios.interceptors.response.use(
response => {
return response
},
error => {
/**
* 状态码401代表无权限访问,权限失效,需要重新获取authToken
* 状态码500代表REST服务器异常
*/
const status = error.response.status
const message = error.response.data.meta.message
if (status === 401) {
/**
* 登录授权token超时,提示
*/
if (message.indexOf('ERROR_CODE_001') > -1) {
this.$message.error(errorCode.ERROR_CODE_001)
}

window.location.href = `${window.location.origin}/login`
return Promise.reject(error)
}

/**
* 1.处理系统服务异常
* 2.处理SoaException异常
*/
if (status === 500) {
if (message.indexOf('ERROR_CODE_003') > -1) {
this.$message.error(errorCode.ERROR_CODE_003)
return
}

this.$message.error(message)
}

return Promise.reject(error)
}
)

封装axios api http.js,便捷方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* http.js
* 封装axios,
* 调用方法:
* http.get('/api/enquiry/web/query',{id:1}).then((res)=>{你的操作})
* http.post('/api/enquiry/web/update',{id:1}).then((res)=>{你的操作})
* http.postFormData('/api/enquiry/web/update',{id:1,file:file}).then((res)=>{你的操作})
*/
import axios from 'axios'

export default {
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
get (url, params) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
}).then(res => {
resolve(res.data)
}).catch(err => {
reject(err)
})
})
},
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
post (url, params) {
return new Promise((resolve, reject) => {
axios.post(url, params)
.then(res => {
resolve(res.data)
})
.catch(err => {
reject(err)
})
})
},
/**
* postFormData方法,对应post请求,用来提交文件+数据
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
postFormData (url, params) {
return new Promise((resolve, reject) => {
axios({
headers: {
'Content-Type': 'multipart/form-data'// ;boundary=----WebKitFormBoundaryQ6d2Qh69dv9wad2u
},
transformRequest: [function (data) { // 在请求之前对data传参进行格式转换
const formData = new FormData()
Object.keys(data).forEach(key => {
formData.append(key, data[key])
})
return formData
}],
url,
method: 'post',
data: params
}).then(res => {
resolve(res.data)
}).catch(err => {
reject(err)
})
})
}
}