在一个大型的vue项目中,需要编写请求接口实在太多太多了,如果每一个接口都从创建实例开始,那代码将会相当庞大以及十分臃肿,后期很难维护,那我们来看看企业级项目中,http请求是怎么抽离出来的吧。
首先是
request.js
,这个文件是用来处理axios的配置、设置拦截器等等,它创建了一个实例,并将这个实例导出。代码如下,注释都写在里面啦
import Vue from 'vue'
import axios from 'axios'
const service = axios.create({
baseURL: '/user',
timeout: 6000
})
service.interceptors.request.use(config => {
const token = Vue.ls.get("ACCESS_TOKEN")
if (token) {
config.headers['X-Access-Token'] = token
}
if (config.method == 'get') {
config.params = {
_t: Date.parse(new Date()) / 1000,
...config.params
}
}
return config
}, (error) => {
return Promise.reject(error)
})
const err = (error) => {
if (error.response) {
switch (error.response.status) {
case 401:
console.log({
message: '系统提示',
description: '未授权,请重新登录',
duration: 4
})
break
case 403:
console.log({
message: '系统提示',
description: '拒绝访问'
})
break
case 404:
console.log({
message: '系统提示',
description: '很抱歉,资源未找到!',
duration: 4
})
break
case 500:
console.log({
message: '系统提示',
description: 'Token失效,请重新登录!'
})
break
case 504:
console.log({
message: '系统提示',
description: '网络超时'
})
break
default:
console.log({
message: '系统提示',
description: error.response.data.message,
})
break
}
}
return Promise.reject(error)
};
service.interceptors.response.use((response) => {
const token = response.headers["authorization"]
if (token) {
Vue.ls.set("ACCESS_TOKEN", token)
}
return response.data
}, err)
export {
service as axios
}
- 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
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
第二个便是
manage.js
,这个文件主要是书写不同的http请求,
get
、
post
等,在请求中配置某些特殊的配置
import { axios } from './request'
export function getAction(url,params) {
return axios({
url: url,
method: 'get',
params: params
})
}
export function postAction(url,data) {
return axios({
url: url,
method:'post' ,
data: data
})
}
export function putAction(url,data) {
return axios({
url: url,
method:'put',
data: data
})
}
export function deleteAction(url,params) {
return axios({
url: url,
method: 'delete',
params: params
})
}
export function downFileAction(url,params){
return axios({
url: url,
params: params,
method:'get' ,
responseType: 'blob'
})
}
export function fileUploadAction(url,data){
return axios({
url: url,
data: data,
method:'post' ,
headers:{
'Content-Type':'multipart/form-data'
},
timeout:1000*60*4
})
}
- 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
最后这个
api.js
文件就是我们需要写的接口了,把接口都写在一个文件中,也是为了方便我们维护,在使用的时候,导入使用便可
import { getAction,deleteAction,putAction,postAction,downFileAction,fileUploadAction} from '@/api/manage'
const getTest = (params)=>getAction("/api/user/get",params);
const deleteActionTest = (params)=>deleteAction("/api/user/delete",params);
const putActionTest = (params)=>putAction("/api/user/put",params);
const postActionTest = (params)=>postAction("/api/user/post",params);
const downFileActionTest = (params)=>downFileAction("/api/user/downfile",params);
const fileUploadActionTest = (params)=>fileUploadAction("/api/user/fileupload",params);
export {
getTest,
deleteActionTest,
putActionTest,
postActionTest,
downFileActionTest,
fileUploadActionTest
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
附带一个项目中用到的文件下载链接处理
axios.get("/api/excel",{id:'001'}).then(res=>{
var blob = new Blob([res],{type: 'application/force-download;charset=utf-8'});
var downloadElement = document.createElement('a');
var href = window.URL.createObjectURL(blob);
downloadElement.href = href;
downloadElement.download = 'name.xls';
document.body.appendChild(downloadElement);
downloadElement.click();
document.body.removeChild(downloadElement);
window.URL.revokeObjectURL(href);
})
axios的正确打开方式
axios是一个基于promise的
HTTP库
,目前已经被主流浏览器其支持(Chrome、Firefox、Safari、Opera、Edge、IE8+)
我们先来举个例子:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
{{ name }}
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
name: "monk"
},
created() {
axios("https://xxxx").then(res=>{
console.log(res)
})
}
})
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
从这个例子就可以看出来,axios的使用非常简单,我们首先需要引入axios,然后直接使用就可以了,axios有两种写法(2个API),一个是例子所示:
axios(url, [config])
,另一个是:
axios(config)
,
config
是一个配置对象。我在下面会介绍如何引入和如何配置axios
引入方式可以使用
cnd引入
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
也可以使用
npm下载后
使用
$ npm install axios
import axios from 'axios'
使用axios
我们使用axios,我总结了三种方式
1、简单粗暴直接使用axios
axios("https://xxxx",{
method:"get"
}).then(res=>{
console.log(res)
})
2、使用axios请求方法的别名
,在使用别名方法时
url
、
method
、
data
这些属性都不必在配置中指定。
axios.get("https://xxxx").then(res=>{
console.log(res)
})
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
3、创建axios实例
,实例上的方法和请求别名相同
const service = axios.create()
service.get("https://monk/api/getUserInfo").then(res=>{
console.log(res)
})
axios配置
我再简单列举一下
axios
的
常用的配置
{
url: "",
method: 'get',
baseURL: 'https://monk/api/',
headers: {},
params: {},
data: {},
timeout: 1000,
responseType: 'json',
}
在对axios进行配置时可以从三个方面进行配置,全局上配置,实例上配置,请求上配置,他们也是有优先级的,
请求配置>实例配置>全局配置
,我来依次介绍一下:
-
全局配置
:但是我们在实际的项目中,很少使用全局配置
axios.defaults.baseURL = 'https://www.monk.com/api';
axios.defaults.timeout = 1000;
const service = axios.create({
baseURL: 'https://www.monk.com/api',
timeout: 1000,
})
service.get('/getUserInfo').then(res => {
console.log(res)
})
const instance = axios.create();
instance.get('https://www.monk.com/api/fileupload', {
timeout: 5000
})
当然,我们也可以混合使用:在普通的请求超时1s,在上传文件时,我们超时5s才阻止请求
const service = axios.create({
baseURL: 'https://www.monk.com/api',
timeout: 1000,
})
service.get('/fileupload', {
timeout: 5000
}).then(res => {
console.log(res)
})
下面我们再来讲解一下
并发
请求,虽然一般用不到
并发
:同时进行多个请求,并统一处理返回值
-
axios.all(iterable)
:
iterable
为需要并发的请求
-
axios.spread(callback)
:
callback
是每个请求的回调
const service = axios.create({
baseURL: 'https://www.monk.com/api',
timeout: 1000,
})
service.all([
axios.get('/a'),
axios.get('/b')
]).then(axios.spread((aRes, bRes) => {
console.log(aRes, bRes);
}))
上述代码是并发执行请求
/a
和
/b
,当请求结束时,在Promise中执行的回调时spread方法,依次调用里面的回调函数。
拦截器
在axios中,拦截器是非常重要的一个部分,那么什么是拦截器呢?
拦截器
interceptors
,在发起请求之前做一些处理,或在响应回来之后做一些处理。
axios.interceptors.request.use(function (config) {
return config;
}, function (error) {
return Promise.reject(error);
})
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
return Promise.reject(error);
})
const myInterceptor = axios.interceptors.request.use(config => {});
axios.interceptors.request.eject(myInterceptor);
const service = axios.create();
service.interceptors.request.use(config => {
return config
});
错误处理
在请求错误时进行的处理,error中有两个属性(上下文),request 和response,在错误中,如果响应有值,说明响应时出现了错误,如果响应没用值,则说明请求出现了错误,如果请求没用值,则说明请求未发出去,比如请求被取消。
axios.get('https:/monk/api/user')
.catch(function (error) {
if (error.response) {
console.log(error.response)
} else if (error.request) {
console.log(error.request)
} else {
console.log('Error', error.message);
}
});
在实际开发过程中,一般在拦截器中统一添加错误处理,请求拦截器中的错误,会当请求未成功发出时执行,但是要注意的是:取消请求后,请求拦截器的错误函数也不会执行,因为取消请求不会抛出异常,axios对其进行了单独的处理。在更多的情况下,我们会在响应拦截器中处理错误。
const instance = axios.create({});
instance.interceptors.request(config => {
}, error => {
return Promise.reject(error);
})
instance.interceptors.response(response => {
}, error => {
return Promise.reject(error);
})
axios 预检
当axios的请求为非简单请求时,浏览器会进行预检,及发送OPTIONS请求。请求到服务器,询问是否允许跨域。如果响应中允许预检中请求的跨域行为,则浏览器会进行真正的请求。否则会报405错误。
响应结构
最后再附带一个axios响应的结构
{
data: {},
status: 200,
statusText: 'OK',
headers: {},
config: {}
}