Axios 简介
Axios 是一个基于 promise 的网络请求库,可作用于 node.js 和浏览器中
特性
- 支持 Promise API
- 客户端支持防御 XSRF - Cross-site request forgery - 跨站请求伪造
- 在服务端,它基于 node.js 的
http
模块;而在客户端 (浏览器),则基于XMLHttpRequests
使用
- node:下载 axios 并导入
npm i axios
const axios = require('axios')
- 浏览器:引入 cdn
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
- 可以打印 axios 实例,查看是否引入/导入成功:
console.log(axios)
axios(config)
url
:请求的服务器 URLmethod
:请求的方法,默认是GET
params
:Get 请求传递的数据 - URL 参数(简单对象 / URLSearchParams 对象)data
:请求体 - 请求携带的参数。仅适用PUT
、POST
、DELETE
、PATCH
请求方法
数据类型:string
、plain object
、ArrayBuffer
、ArrayBufferView
、URLSearchParams
浏览器专属:FormData
、File
、Blob
;Node 专属:Stream
、Buffer
baseURL
:基本路径。设置了baseURL
后,url
可以写相对路径timeout
:请求超时的毫秒数。如果请求时间超过timeout
值,请求会被中断。默认为0
(永不超时)headers
:自定义请求头。eg:{'X-Requested-With': 'XMLHttpRequest'}
responseType
:响应的数据类型,默认为json
还可以是:arraybuffer
、document
、text
、stream
、blob
(浏览器专属)
- 如果
config
只设置url
,url
可以直接作为axios()
的参数
<!-- demo.html -->
<body>
<div id="app">
<button @click="request">点击发送请求</button>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
let app = new Vue({
el: "#app",
methods: {
request() {
// 发送 axios
axios({
url: "http://127.0.0.1:8080/test", // 设置 URL
method: "POST", // 设置请求方式
data: { axios: "(req1)" }, // 设置请求体
}).then(res => {
console.log(res.data); // (req1)[res]
});
},
},
});
</script>
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
// 设置路由
server.get("/", (req, res) => {
res.sendFile(__dirname + "/demo.html"); // 响应页面
res.end; // 关闭请求
});
// 设置响应格式
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
// extended: false - 使用系统模块 querystring 来处理 (官方推荐)
// extended: true - 使用第三方模块 qs 来处理
// 从功能性来讲,qs 比 querystring 更强大,这里可以根据项目的实际需求来考虑
// 处理 post 请求
server.post("/test", (req, res) => {
// 通过 req.body 获取 post 请求传递过来的数据
console.log(req.body); // { axios: '(req1)' }
res.send(req.body.axios + "[res]"); // 响应请求
res.end; // 关闭请求
});
搭建好服务器后,直接到 http:127.0.0.1:8080
即可进行交互
响应对象
res
的结构
data
:服务器响应的数据status
:HTTP 状态码statusText
:HTTP 状态信息headers
:服务器响应头config
:axios 请求的配置信息
res {data: '(req1)[res]', status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
axios.get(url[, config])
URL
:发送的路径config
:配置信息
request() {
// 发送 get 请求
axios.get("http://127.0.0.1:8080/test", {
params: { axios: "(req1)" }, // 设置 get 请求传递的数据,即 URL 参数
}).then(res => {
console.log(res.data); // (req1)[res]
});
},
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
server.get("/", (req, res) => {
res.sendFile(__dirname + "/demo.html");
res.end;
});
// 处理 get 请求
server.get("/test", (req, res) => {
// 通过 req.query 获取 URL 参数
console.log(req.query); // { axios: '(req1)' }
res.send(req.query.axios + "[res]");
res.end;
});
axios.post(url[, data[, config]])
URL
:发送的路径data
:请求体config
:配置信息
request() {
axios.post("http://127.0.0.1:8080/test", {
axios: "(req1)", // 设置 post 请求传递的参数
}).then(res => {
console.log(res.data); // (req1)[res]
});
},
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
server.get("/", (req, res) => {
res.sendFile(__dirname + "/demo.html");
res.end;
});
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.post("/test", (req, res) => {
console.log(req.body); // { axios: '(req1)' }
res.send(req.body.axios + "[res]");
res.end;
});
axios.all(axiosList)
axios.all(axiosList)
会等 axiosList
里所有的请求都被成功响应后,才会返回响应结果
request() {
axios.all([
axios.post("http://127.0.0.1:8080/test", {
axios: "(req1)",
}),
axios.post("http://127.0.0.1:8080/test", {
axios: "(req2)",
}),
axios.post("http://127.0.0.1:8080/test", {
axios: "(req3)",
}),
]).then(res => {
console.log("res", res);
// res [{…}, {…}, {…}]
// 0: {data: '(req1)[res]', status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
// 1: {data: '(req2)[res]', status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
// 2: {data: '(req3)[res]', status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
});
},
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
server.get("/", (req, res) => {
res.sendFile(__dirname + "/demo.html");
res.end;
});
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.post("/test", (req, res) => {
console.log(req.body);
// { axios: '(req1)' }
// { axios: '(req2)' }
// { axios: '(req3)' }
res.send(req.body.axios + "[res]");
res.end;
});
链式调用
当后面的 ajax 需要使用到前面的 ajax 返回的数据时,我们可以使用 axios 的链式调用
request() {
axios({
url: "http://127.0.0.1:8080/test",
method: "GET",
params: { axios: "(req1)" },
}).then(data1 => {
console.log(data1.data); // (req1)[res]
return axios({
url: "http://127.0.0.1:8080/test",
method: "GET",
params: { axios: data1.data + "(req2)" },
});
}).then(data2 => {
console.log(data2.data); // (req1)[res](req2)[res]
return axios({
url: "http://127.0.0.1:8080/test",
method: "GET",
params: { axios: data2.data + "(req3)" },
});
}).then(data3 => {
console.log(data3.data); // (req1)[res](req2)[res](req3)[res]
});
},
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
server.get("/", (req, res) => {
res.sendFile(__dirname + "/demo.html");
res.end;
});
server.get("/test", (req, res) => {
console.log(req.query);
// { axios: '(req1)' }
// { axios: '(req1)[res](req2)' }
// { axios: '(req1)[res](req2)[res](req3)' }
res.send(req.query.axios + "[res]");
res.end;
});
上面的 axios 链式调用也可以用 async await 函数实现
async request() {
let data1 = await axios({
url: "http://127.0.0.1:8080/test",
method: "GET",
params: { axios: "(req1)" },
});
console.log(data1.data); // (req1)[res]
let data2 = await axios({
url: "http://127.0.0.1:8080/test",
method: "GET",
params: { axios: data1.data + "(req2)" },
});
console.log(data2.data); // (req1)[res](req2)[res]
let data3 = await axios({
url: "http://127.0.0.1:8080/test",
method: "GET",
params: { axios: data2.data + "(req3)" },
});
console.log(data3.data); // (req1)[res](req2)[res](req3)[res]
},
默认配置
指定默认配置,它将作用于每个请求:
全局 axios 默认值
axios.defaults.baseURL = "https://www.example.site/demo";
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
歪个楼,聊一聊 "Content-type":
Content-Type (内容类型),用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
Content-Type 标头告诉客户端实际返回的数据类型。常见的 "content-type" 值:
媒体格式类型:
1. "text/html":HTML 格式
2. "text/plain":纯文本格式
3. "text/xml":XML 格式
4. "image/gif":gif 图片格式
5. "image/jpeg":jpg 图片格式
6. "image/png":png 图片格式
以 application 开头的媒体格式类型:
1. "application/json":JSON 数据格式
2. "application/xml":XML 数据格式
3. "application/xhtml+xml":XHTML 格式
4. "application/pdf":pdf 格式
5. "application/msword":Word 文档格式
6. "application/octet-stream":二进制流数据
7. "application/x-www-form-urlencoded":<form encType=""> 中默认的 encType,form 表单数据被编码为 key/value 格式发送 到服务器(表单默认的提交数据的格式)
另外一种常见的媒体格式是上传文件之时使用的:
1. "multipart/form-data":需要在表单中进行文件上传时,就需要使用该格式
- 编码可带可不带:"application/json; charset=utf-8"
- 值对大小写不敏感:"Application/JSON; charset=utf-8"
自定义 axios 实例默认值
// 创建 axios 实例时配置默认值
const instance = axios.create({ baseURL: 'https://api.example.com' });
// 创建实例后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
优先级:config > 实例默认值 > 全局默认值 > 属性默认值
// 属性默认值:timeout 为 0
const instance = axios.create();
// 实例默认值 > 属性默认值:timeout 现在为 2500
instance.defaults.timeout = 2500;
instance.get('/longRequest', {
timeout: 5000
// config > 实例默认值:timeout 最后为 5000
});
axios 实例
- 我们可以使用自定义配置新建一个实例。创建 axios 实例:
axios.create([config])
request() {
const instance = axios.create({
baseURL: "http://127.0.0.1:8080",
timeout: 5000,
});
instance({
url: "/test",
method: "GET",
params: { axios: "req1" },
}).then(res => {
console.log(res.data); // req1[res]
});
},
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
server.get("/", (req, res) => {
res.sendFile(__dirname + "/demo.html");
res.end;
});
server.get("/test", (req, res) => {
console.log(req.query); // { axios: 'req1' }
res.send(req.query.axios + "[res]");
res.end;
});
解决跨域问题
歪个楼,解释一下跨域:"协议"、"域名"、"端口号",三者有一不一样,就是跨域
- 注意:通过服务器打开的页面,与服务器是同源的,所以上面这些例子都能完成数据的传输
- 下例的页面是通过 VSCode 的插件 Live Server 打开的
此时 [页面] 的地址为http://127.0.0.1:5500/demo.html
;[服务器] 的地址为http://127.0.0.1:8080
可以看到,[页面] 与 [服务器] 的端口号不一样,这种情况下 [页面] 与 [服务器] 是不能交互数据的 - 跨域情况下,数据的其实是能交互的,只是数据返回给浏览器时,浏览器会拦截跨域的数据,使你无法获取
后端配置 cors
-
CORS - Cross-Origin Resource Sharing,跨域资源共享
-
CORS 是官方的跨域解决方案,不需要客户端做任何操作,完全在服务器中处理
-
CORS 通过设置响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应后,就会对其响应放行
response.setHeader('Access-Control-Allow-Origin', '*')
:指示请求的资源能共享给哪些域response.setHeader('Access-Control-Allow-Headers', '*')
:指示哪些 HTTP 头的名称能在响应中列出response.setHeader('Access-Control-Allow-Methods', '*')
:明确客户端所要访问的资源允许使用的方法
使用
- 安装 cors:
npm i cors
- 配置 cors:
const cors = require('cors'); server.use(cors());
request() {
axios({
url: "http://127.0.0.1:8080/test",
method: "POST",
data: { axios: "(req1)" },
}).then(res => {
console.log(res.data); // (req1)[res]
});
},
/* app.js */
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
// 解决 ajax 跨域问题
const cors = require("cors");
server.use(cors()); // 所有域名均可跨域请求
// server.use(cors( origin: "http://demo.com" )) // 指定 1 ~ n 个域名可跨域请求
// server.use(cors( origin: ["http://demo1.com", "http://demo2.com"] ))
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.post("/test", (req, res) => {
console.log(req.body); // { axios: '(req1)' }
res.send(req.body.axios + "[res]");
res.end();
});
jsonp
- JSONP 全称 JSON with Padding,是一个非官方的跨域解决方案
- 在网页中,有一些标签天生就有跨域能力,eg:
img
、link
、iframe
、script
JSONP 就是利用script
标签的跨域能力来发送请求 - 所谓 JSONP,就是在本地定义一个函数
然后通过script:src
引入一个服务器上的 JS 文件,该文件中刚好有同名函数执行,通过传参进行数据的传递 - 使用 JSONP 解决跨域问题时,响应体必须是 JS 语句。因为 script 标签是用来传输 JS 文件的,只识别 JS 语句
- 只能解决 GET 请求的跨域问题
- 需要前后端都进行相应配置
<!-- demo.html -->
<style>
#result {
width: 200px;
height: 100px;
background: lemonchiffon;
}
</style>
<body>
<div id="result"></div>
<script>
function handle(data) {
let result = document.getElementById('result');
result.innerHTML = data.name;
console.log(data); // {name: 'superman'}
}
</script>
</body>
<script src="http://127.0.0.1:8000/jsonp-server"></script>
// app.js
const express = require("express");
let app = express();
// 创建路由规则
app.all("/jsonp-server", (request, response) => {
let content = { name: "superman" };
let str = JSON.stringify(content);
response.send(`handle(${str})`); // 响应回 JS 语句
});
// 监听端口启动服务
app.listen(8000, () => { console.log("服务已经启动,8000 端口监听中...") });
上述代码中,函数 handle
与其执行语句不同源,执行语句是通过 script 标签引入的
此时,handle
必须定义在页面文件的全局作用域下
代理服务器
- 代理服务器 eg:nginx、vue-cli…
- 因为跨域是浏览器的保护机制,如果脱离了浏览器发送请求,就不会受到跨域保护机制的影响
- 代理服务器所处的位置与前端页面的位置一样,所以没有跨域问题
- 服务器之间交互,使用传统的 HTTP 请求即可;不需要 Ajax,所以也没有跨域问题
- 前端 → 代理服务器(请求)
- 代理服务器 → 服务器(请求)
- 服务器 → 代理服务器(响应)
- 代理服务器 → 前端(响应)
vue-cli 代理一台服务器
① 配置服务器:
const express = require('express');
let app = express();
app.listen(5000, () => console.log('http://localhost:5000'));
app.get('/students', (req, res) => {
console.log("5000 path", req.path);
let dataArr = [
{ id: "001", name: "superman1" },
{ id: "002", name: "superman2" },
{ id: "003", name: "superman3" }
];
res.send(dataArr);
});
② 配置 vue.config.js 设置 vue-cli 的代理服务器:
注意:修改配置文件 vue.config.js 后需要重启 vue 项目
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
// 开启代理服务器
devServer: {
proxy: "http://127.0.0.1:5000", // 设置需要代理的服务器
},
});
③ 使用代理服务器:
注意:需要 yarn add axios
/ npm i axios
<template>
<div>
<button @click="getStudents">获取学生信息</button>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
methods: {
getStudents() {
axios.get("/students").then(
res => console.log("res", res),
reason => console.log("reason", reason)
);
},
},
};
</script>
如此,我们获取数据时,如果前端 ( public 文件夹 ) 有该数据资源,则直接获取前端资源
如果前端没有该数据资源,则发送请求给 [被代理的服务器] 获取数据(优先匹配前端资源)
我们直接访问 8080 端口时,其实是默认访问了 public 文件夹下的 index.html
vue-cli 可以通过 [请求前缀] 代理多个服务器
① 再配置一台服务器
const express = require("express");
let app = express();
app.listen(5001, () => console.log("http://localhost:5001"));
app.get("/books", (req, res) => {
console.log("5001 path", req.path);
let dataArr = [
{ id: "001", name: "JS" },
{ id: "002", name: "Node" },
{ id: "003", name: "Vue" },
];
res.send(dataArr);
});
② 配置 vue.config.js 设置 vue-cli 的代理服务器:
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
devServer: {
proxy: {
// 设置 [请求前缀],以代理多台服务器
"/students": { target: "http://localhost:5000" },
"/books": { target: "http://localhost:5001" },
},
},
});
该写法中,[请求前缀] 既是区分服务器的标识,也是要请求的路径
即:前端仍通过 http://127.0.0.1:8080/url路径
获取数据
获取数据时,仍是优先获取前端资源;前端没有该资源,才发送请求给 [被代理的服务器] 获取数据
③ 使用代理服务器:
<template>
<div>
<button @click="getStudents">获取学生信息</button>
<button @click="getBooks">获取教材信息</button>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
methods: {
getStudents() {
axios.get("/students").then(
res => console.log("res", res),
reason => console.log("reason", reason)
);
},
getBooks() {
axios.get("/books").then(
res => console.log("res", res),
reason => console.log("reason", reason)
);
},
},
};
</script>
- 我们也可以设置 [请求前缀] 仅为服务器的标识:
② 配置 vue.config.js 设置 vue-cli 的代理服务器:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
devServer: {
proxy: {
'/api1': {
target: 'http://localhost:5000',
pathRewrite: { '^/api1': '' } // 隐藏 [请求前缀],保证请求地址能正确使用
},
'/api2': {
target: 'http://localhost:5001',
pathRewrite: { '^/api2': '' } // 隐藏 [请求前缀],保证请求地址能正确使用
},
}
}
});
上例写法中,[请求前缀] 仅为服务器的标识,只要带上 [请求前缀] 就不会匹配前端资源
此时,我们发送请求时,就需要带上 [请求前缀] 以选择发送请求的服务器 http://127.0.0.1:8080/api1/url路径
③ 使用代理服务器
<template>
<div>
<button @click="getStudents">获取学生信息</button>
<button @click="getBooks">获取教材信息</button>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
methods: {
getStudents() {
// 发送请求时带上 [请求前缀]
axios.get("/api1/students").then(
res => console.log("res", res),
reason => console.log("reason", reason)
);
},
getBooks() {
// 发送请求时带上 [请求前缀]
axios.get("/api2/books").then(
res => console.log("res", res),
reason => console.log("reason", reason)
);
},
},
};
</script>
取消请求
const controller = new AbortController(); // 创建 AbortController 实例
axios.get('/foo/bar', {
signal: controller.signal // 配置 signal 字段
}).then(response => {
//...
});
// 调用 abort 方法
controller.abort()
demo
<body>
<div id="app">
<button @click="request">点击发送请求</button>
<button @click="cancel">点击取消请求</button>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
let app = new Vue({
el: "#app",
data() {
return { controller: null };
},
methods: {
request() {
this.controller = new AbortController(); // 创建 AbortController 实例
axios({
url: "http://127.0.0.1:8080/test",
method: "POST",
data: { axios: "(req1)" },
signal: this.controller.signal, // 配置 signal 字段
}).then(
res => console.log(res.data),
reason => console.log("reason", reason)
);
},
cancel() {
this.controller.abort(); // 调用 abort 方法
},
},
});
</script>
const express = require("express");
let server = express();
server.listen(8080, () => console.log("http://127.0.0.1:8080"));
const cors = require("cors");
server.use(cors());
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.all("/test", (req, res) => {
console.log(req.body);
setTimeout(() => {
res.send(req.body.axios + "[res]");
res.end();
}, 2000);
});
拦截器
// 请求拦截器
axios.interceptors.request.use(config => {
// 在发送请求之前做些什么
return config; // config 为发送的请求
}, error => {
// 对请求错误做些什么
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(response => {
// 2xx 范围内的状态码都会触发该函数
// 对响应数据做点什么
return response; // response 为接收的响应
}, error => {
// 超出 2xx 范围的状态码都会触发该函数
// 对响应错误做点什么
return Promise.reject(error);
});
eject
:用于移除拦截器
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
demo:将 token 配置到请求头
axios.interceptors.request.use(config => {
// 如果 localStorage 中有 "token" 值,则将其配置到请求头中
localStorage.getItem("token") && (config.headers.token = localStorage.getItem("token"));
return config;
}, error => {
return Promise.reject(error);
});