使用Axios接口请求及及文件上传

安装

方式1

1
npm install axios

引用

1
import axios from "axios";

方式2

1
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

调用

1
2
3
window.axios.get("https://www.psvmc.cn").then(res => {
console.info(res);
})

axios实例

建议不同的接口配置不同的实例,特别时普通接口请求和文件接口。

主要是因为:

  • 接口地址不一样。
  • 超时时间不一样。

配置文件

public文件夹中放我们的配置文件

1
window.baseURL = "http://192.168.3.80:9999/";

index.html中引用

1
<script src="./config.js"></script>

注意

如果使用导入导出的方式,打包后修改配置文件也不生效。

工具类

src/assets/js/axios_utils.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
import axios from "axios";

const http = axios.create({
baseURL: window.baseURL,
timeout: 1000 * 10, // 请求超时时间10秒(单位:毫秒)
validateStatus: function (status) {
return status >= 200 && status <= 500; // 默认的
},
headers: {
"Content-Type": "application/json",
},
});

http.interceptors.request.use(
(config) => {
if (!window.loginUser) {
let wiki_loginuser = localStorage.getItem("wiki_loginuser");
if (wiki_loginuser) {
window.loginUser = JSON.parse(wiki_loginuser) || {};
} else {
window.loginUser = null;
}
}
if (window.loginUser) {
config.headers["Z-UserId"] = window.loginUser.userid;
}

return config;
},
(err) => {
return Promise.reject({
code: 1,
msg: err.message || "请求失败",
});
}
);

http.interceptors.response.use(
(response) => {
if (response.status === 200) {
return response.data;
} else {
return {
code: 1,
msg: "请求失败",
};
}
},
(error) => {
return Promise.reject({
code: 1,
msg: error.message || "请求失败",
});
}
);

export default http;

调用

调用的接口

src/assets/js/m_api.js

1
2
3
4
5
6
import axios from "@/assets/js/axios_utils";

export const api_user_login = data=>{
let req_url = "user/login_teacher";
return axios.post(req_url, data);
}

页面中获取配置项或接口

1
import { api_user_login } from "@/assets/js/m_api";

几种传参方式

URL传参

1
let url = `/login?userid=${this.userId}&subject=${this.subject}`

application/json

如果没有特别声明,application/json是Axios默认的Content-Type,也是最常用的一种,它声明了请求体中的数据将会以json字符串的形式发送到后端。

1
2
3
4
5
6
7
8
9
10
var params = {
"range": {
"length": 100,
"start": 0
},
"branchId": '100001'
};
this.$axios.post("/XXXX/XXXX", params).then(data => {
//To Do
});

application/x-www-form-urlencoded

Content-Type:application/x-www-form-urlencoded,则声明了请求体中的数据会以键值对(普通表单形式)发送到后端,这种类型是Ajax默认的。

偶尔后端需要我要传键值对给他们,那这个时候,就需要在头部设置headers: { 'Content-Type':'application/x-www-form-urlencoded'},

然后,将请求体中的数据设置为键值对。但是我们封装的请求体一般是JSON对象,这个时候,就可以使用qs库将对象转为url参数形式。

请求示例

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
import axios from "axios";
import qs from "qs";

async tokenActivation() {
let paras = {
activateType: "2",
activateCode: "A3JUREKM",
stage: this.subject.substring(0, 2),
subject: this.subject,
};
let res = await axios.post(
`/activation/paper-generator/vip/open`,
qs.stringify(paras),
{
headers: {
Canpointtoken: this.token,
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
if (res.status === 200) {
let data = res.data;
console.info(data);
}
},

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import axios from "axios";
import qs from "qs";
var data = {
"username": "admin",
"password": "123456"
}

axios({
url: '/OAuth/oauth/token',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: qs.stringify(data)
})

qs是Axios默认就有的,就不需要再npm了。

1
import qs from 'qs';

QS的用法

参数字符串转对象

1
2
3
var url='userId=admin&password=hellworld';
// 转为对象
console.log(qs.parse(url));

对象转参数字符串

1
2
var person = {name:'lihua',age:18};
qs.stringify(person)

multipart/form-data

只要使用FormData对象,axios就会把请求头中设置为'Content-Type': 'multipart/form-data'

1
2
3
4
5
6
7
8
9
10
11
12
let formData = new FormData();
formData.append('file', document.querySelector('input[type=file]').files[0]);
formData.append('username', 'admin');
formData.append('password', '123456');
axios
.post(
url,
formData
)
.then((res) => {
console.info(res);
});

当然我们也可以自己指定

1
2
3
4
5
6
7
8
9
10
11
12
13
let formData = new FormData();
formData.append('file', document.querySelector('input[type=file]').files[0]);
formData.append('username', 'admin');
formData.append('password', '123456');

axios({
url: '/XXXX/XXXX',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: formData
})

headers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let headers = {
apiId: apiId,
accessKeyID: this.accessKeyID,
appKey: this.appKey,
timestamp: timestamp,
signature: signature,
};
axios
.post(
url,
{},
{
headers: headers,
}
)
.then((res) => {
console.info(res);
});

文件上传

单独文件上传

HTML

1
<input class="file" name="file" type="file" accept="image/png,image/gif,image/jpeg" @change="uploadFileAction"/>

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import axios from "axios";

uploadFileAction(e){
let file = e.target.files[0];
let param = new FormData(); //创建form对象
param.append('file',file);//通过append向form对象添加数据
console.log(param.get('file')); //FormData私有类对象,访问不到,可以通过get判断值是否传进去
let config = {
headers:{'Content-Type':'multipart/form-data'}
}; //添加请求头
axios.post('http://127.0.0.1:8081/upload',param,config)
.then(response=>{
console.log(response.data);
})
}

其中file对象中我们能取到

1
2
3
name: "7.png"
size: 21390
type: "image/png"

图片校验

1
2
3
4
5
6
7
8
9
10
11
12
uploadImg(e) {
const file = e.target.files[0]
if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
this.$Message.error('请上传.png/.jpg/.jpeg格式的图片');
e.target.value = null;
return false
} else if (file.size / 1024 > 400) {
this.$Message.error('请上传不超过400KB的图片');
e.target.value = null;
return false
}
}

zip

1
2
3
4
5
6
7
8
9
10
11
12
uploadImg(e) {
const file = e.target.files[0]
if (!/\.(zip|ZIP)$/.test(e.target.value)) {
this.$Message.error('请上传.zip格式的文件');
e.target.value = null;
return false
} else if (file.size / 1024 > 1024) {
this.$Message.error('请上传不超过1M的文件');
e.target.value = null;
return false
}
}

注意不符合要求时清空所选文件

1
e.target.value = null;

图片预览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uploadImg(e) {
const reader = new FileReader()
reader.onload = e => {
let data
if (typeof e.target.result === 'object') {
// 把Array Buffer转化为blob 如果是base64不需要
data = window.URL.createObjectURL(new Blob([e.target.result]))
} else {
data = e.target.result
}
this.option.img = data
}
// 转化为blob
reader.readAsArrayBuffer(file)
}

表单文件上传

HTML

1
2
3
4
5
6
<form>
<input type="text" value="" v-model="name" placeholder="请输入用户名">
<input type="text" value="" v-model="age" placeholder="请输入年龄">
<input type="file" @change="getFile($event)">
<button @click="submitForm($event)">提交</button>
</form>

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
data: {
name: '',
age: '',
file: ''
},
methods: {
getFile(event) {
this.file = event.target.files[0];
console.log(this.file);
},
submitForm(event) {
event.preventDefault();
let formData = new FormData();
formData.append('name', this.name);
formData.append('age', this.age);
formData.append('file', this.file);

let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}

this.$http.post('http://127.0.0.1:8081/upload', formData, config).then(function (response) {
if (response.status === 200) {
console.log(response.data);
}
})
}
}

拦截器

1
2
3
4
5
6
7
8
9
10
11
const axios = require("axios");

const http = axios.create({
timeout: 1000 * 10, // 请求超时时间10秒(单位:毫秒)
validateStatus: function (status) {
return status >= 200 && status <= 500; // 默认的
},
headers: {
"Content-Type": "application/json",
},
});

请求拦截器

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
http.interceptors.request.use(
(config) => {
if (
aesopen &&
config.headers["Content-Type"] &&
config.headers["Content-Type"].indexOf("application/json") !== -1 &&
config.method === "post" &&
config.data
) {
config.headers.st = aesopen;
config.data = encStr(JSON.stringify(config.data));
}

if(usetoken){
let userInfoStr = localStorage.getItem("school-live-client-userinfo");
if (userInfoStr) {
let userInfo = JSON.parse(userInfoStr);
if(userInfo){
config.headers["Token-Value"] = userInfo.token_value || "";
config.headers["Token-Key"] = userInfo.cloudcode+"_"+userInfo.userid+"_live_pc_t";
}

}
}
return config;
},
(err) => {
return Promise.reject({
code: 1,
msg: err.message || "请求失败"
});
}
);

响应拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http.interceptors.response.use(
response => {
if (response.data.code === 2) {
setTimeout(() => {
ipcRenderer.send("turnLoginWin");
}, 3 * 1000);
}
return response;
},
error => {
return Promise.reject({
code: 1,
msg: error.message || "请求失败"
});
}
);

自定义请求头Header

1
axios.defaults.headers.post["version"] = window.baseconfig.version;

或者

1
2
3
4
5
6
7
8
9
10
11
12
http.interceptors.request.use(
(config) => {
config.headers["version"] = window.baseconfig.version;
return config;
},
(err) => {
return Promise.reject({
code: 1,
msg: err.message || "请求失败",
});
}
);

或者

1
2
3
4
5
6
7
8
9
const http = axios.create({
timeout: 1000 * 10, // 请求超时时间10秒(单位:毫秒)
validateStatus: function (status) {
return status >= 200 && status <= 500; // 默认的
},
headers: {
"Content-Type": "application/json",
},
});

添加自定义Header需要后端接口允许,否则会报错

Request header field version is not allowed by Access-Control-Allow-Headers in preflight response.

后端

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
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class CORSInterceptor implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
String origin = (String) servletRequest.getRemoteHost() + ":" + servletRequest.getRemotePort();
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
filterChain.doFilter(servletRequest, servletResponse);

}

@Override
public void destroy() {
}
}

获取响应头

使用 respose.headers 拿到的只用两个默认的headers, 尝试了使用捕获响应头的方法

1
2
3
4
5
6
axios.interceptors.response.use(function (response) {
console.log(response);
return response;
}, function (error) {
return Promise.reject(error);
});

结果打印出来的还是

1
2
3
4
Object {
cache-control:"private, must-revalidate",
content-type:"application/json"
}

原因是:

在默认的请求上, 浏览器只能访问以下默认的响应头

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

如果想让浏览器能访问到其他的响应头的话 需要在服务器上设置 Access-Control-Expose-Headers

1
Access-Control-Expose-Headers : 'Authorization'

多个用逗号分割

1
Access-Control-Expose-Headers : "Authorization,Cache"

前端成功获取Authorization

Java代码

1
response.setHeader("Access-Control-Expose-Headers", "Authorization");

但是注意的是

响应中的首字母大写,在获取的时候变成了小写,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
myhttp.interceptors.response.use(
(response) => {
if (response.status === 200) {
if (response.headers.authorization) {
localStorage.setItem("Authorization", response.headers.authorization);
}
return response.data;
}
return response;
},
(error) => {
return Promise.reject({
code: 1,
message: error.message || "请求失败",
});
}
);

请求体多了双引号

提交body raw格式

全局配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const http = axios.create({
timeout: 1000 * 10, // 请求超时时间10秒(单位:毫秒)
validateStatus: function (status) {
return status >= 200 && status <= 500; // 默认的
},
transformRequest: [
function (data) {
if (Object.prototype.toString.call(data) === '[object Object]') {
return JSON.stringify(data);
} else if (Object.prototype.toString.call(data) === '[object String]') {
return data;
}
return data;
}
],
headers: {
"Content-Type": "application/json",
},
});

单个请求

1
2
3
4
5
6
7
8
9
10
axios.post('/air/point/saveOrUpdate', JSON.stringify(params), {
transformRequest: [
function(data) {
return data
}
],
headers: {
'Content-Type': 'application/json'
}
})

在做接口加密的时候,如下我们把请求体加密为字符串了,但是发起请求的时候,字符串会多了两个双引号,

原因是

axios会把请求题转换为设置的请求体格式,因为设置的是JSON所以,他就转成了JSON对象。

如下:

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
http.interceptors.request.use(
(config) => {
if (
aesopen &&
config.headers["Content-Type"] &&
config.headers["Content-Type"].indexOf("application/json") !== -1 &&
config.method === "post" &&
config.data
) {
config.headers.st = aesopen;
if (typeof (config.data) !== 'string') {
let bodyData = JSON.stringify(config.data)
let strEnc = encStr(bodyData);
config.data = strEnc;
}
}
return config;
},
(err) => {
return Promise.reject({
code: 1,
msg: err.message || "请求失败"
});
}
);