简介
https://www.axios-http.cn/docs/intro
安装
方式1
引用
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, validateStatus: function (status) { return status >= 200 && status <= 500; }, headers: { "Content-Type": "application/json", }, });
http.interceptors.request.use( (config) => { if (!window.loginUser) { let sysLoginUser = localStorage.getItem("sysLoginUser"); if (sysLoginUser) { window.loginUser = JSON.parse(sysLoginUser) || {}; } 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 => { });
|
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了。
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)
|
只要使用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 })
|
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); });
|
文件上传
方法
api_common.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import axios from './axios_utils.js'
export function apiFileUpload( file: File, savefolder: string ): Promise<ZResult<string>> {
const param = new FormData() param.append('file', file) param.append('savefolder', savefolder)
const config = { headers: { 'Content-Type': 'multipart/form-data' } } return axios.post('/file/upload', param, config) }
|
单独文件上传
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(); param.append('file',file); console.log(param.get('file')); 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 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') { data = window.URL.createObjectURL(new Blob([e.target.result])) } else { data = e.target.result } this.option.img = data } 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, 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 || "请求失败" }); } );
|
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, 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, 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 || "请求失败" }); } );
|
Nginx允许自定义响应标头
注意如果接口已经配置过了,Nginx上就不要配置,否则会导致接口无法请求。
Nginx中在响应中添加如下Header
1 2 3 4 5 6 7 8
| location / { add_header 'Access-Control-Allow-Origin' $http_origin always; add_header 'Access-Control-Allow-Methods' 'GET,OPTIONS,POST' always; add_header 'Access-Control-Allow-Headers' '*'; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Max-Age' 36000; if ($request_method = OPTIONS ) { return 200; } }
|
其中:
Access-Control-Allow-Headers是允许的响应标头。
Access-Control-Max-Age,用来指定本次预检请求的有效期,单位为秒。上面配置中,有效期是10小时(36000秒),在此期间,不用发出另一条预检请求。
Access-Control-Allow-Origin 只能设置为三种情况:星号 *、单域名、none。同时还有一个限制就是设置为星号的时候,Access-Control-Allow-Credentials 不能设置为true。
Access-Control-Allow-Credentials 则一般是服务器用来设置是否允许前端携带Cookies的标志位。
TS中使用
定义接口的返回类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| interface ZResult<T> { code: number msg: string obj: T }
interface ZLoginUser { userphone: string usertype: number storename: string | null storeid: number | null userid: number username: string }
|
公共接口文件
common_api.ts
1 2 3 4 5
| import axios from './axios_utils.js'
export function apiUserLogin(userphone: string, msgcode: string): Promise<ZResult<ZLoginUser[]>> { return axios.post('/user/login', { userphone, msgcode }) }
|