【Ajax】Axios 的使用

Axios 简介

Axios 是一个基于 promise 的网络请求库,可作用于 node.js 和浏览器中

特性
  1. 支持 Promise API
  2. 客户端支持防御 XSRF - Cross-site request forgery - 跨站请求伪造
  3. 在服务端,它基于 node.js 的 http 模块;而在客户端 (浏览器),则基于 XMLHttpRequests
使用
  1. node:下载 axios 并导入 npm i axios const axios = require('axios')
  2. 浏览器:引入 cdn <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  • 可以打印 axios 实例,查看是否引入/导入成功:console.log(axios)

axios(config)

  • config:配置对象
  1. url:请求的服务器 URL
  2. method:请求的方法,默认是 GET
  3. params:Get 请求传递的数据 - URL 参数(简单对象 / URLSearchParams 对象)
  4. data:请求体 - 请求携带的参数。仅适用 PUTPOSTDELETEPATCH 请求方法
    数据类型:stringplain objectArrayBufferArrayBufferViewURLSearchParams
    浏览器专属:FormDataFileBlob;Node 专属:StreamBuffer
  5. baseURL:基本路径。设置了 baseURL 后,url 可以写相对路径
  6. timeout:请求超时的毫秒数。如果请求时间超过 timeout 值,请求会被中断。默认为 0 (永不超时)
  7. headers:自定义请求头。eg:{'X-Requested-With': 'XMLHttpRequest'}
  8. responseType:响应的数据类型,默认为 json
    还可以是:arraybufferdocumenttextstreamblob(浏览器专属)
  • 如果 config 只设置 urlurl 可以直接作为 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 的结构
  1. data:服务器响应的数据
  2. status:HTTP 状态码
  3. statusText:HTTP 状态信息
  4. headers:服务器响应头
  5. config:axios 请求的配置信息
res {data: '(req1)[res]', status: 200, statusText: 'OK', headers: {}, config: {},}

axios.get(url[, config])

  1. URL:发送的路径
  2. 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]])

  1. URL:发送的路径
  2. data:请求体
  3. 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

  1. CORS - Cross-Origin Resource Sharing,跨域资源共享

  2. CORS 是官方的跨域解决方案,不需要客户端做任何操作,完全在服务器中处理

  3. CORS 通过设置响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应后,就会对其响应放行

    response.setHeader('Access-Control-Allow-Origin', '*'):指示请求的资源能共享给哪些域

    response.setHeader('Access-Control-Allow-Headers', '*'):指示哪些 HTTP 头的名称能在响应中列出

    response.setHeader('Access-Control-Allow-Methods', '*'):明确客户端所要访问的资源允许使用的方法

使用
  1. 安装 cors:npm i cors
  2. 配置 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:imglinkiframescript
    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,所以也没有跨域问题
  1. 前端 → 代理服务器(请求)
  2. 代理服务器 → 服务器(请求)
  3. 服务器 → 代理服务器(响应)
  4. 代理服务器 → 前端(响应)
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);
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JS.Huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值