系列学习前端之第 7 章:一文掌握 AJAX

1、AJAX 简介

  • AJAX 全称为 Asynchronous JavaScript And XML(中文名:阿贾克斯),就是异步的 JS 和 XML。

  • AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。

  • AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。

  • AJAX 的两个主要功能使您可以执行以下操作:

  • 向服务器发出请求,而无需重新加载页面

  • 从服务器接收和处理数据

2、AJAX 的优缺点

2.1 AJAX 的优点

  • 可以无需刷新页面而与服务器端进行通信。

  • 允许你根据用户事件来更新部分页面内容。

2.2 AJAX 的缺点

  • 没有浏览历史,不能回退。
  • 存在跨域问题(同源)。出于安全原因,浏览器不允许跨域访问。也就是当前网页和 AJAX 要请求的网页地址要在同一个域名下。
  • SEO(搜索引擎优化) 不友好。因为源代码没有具体信息,而是通过 AJAX 异步获取数据,无法被爬虫获取到数据

3、AJAX 的工作原理

JavaScript 使用一个 XMLHttpRequest(核心对象) 对象向服务器发出 HTTP 请求并作为响应接收数据。所有现代浏览器(Chrome,Firefox,IE7 +,Safari,Opera)都支持该 XMLHttpRequest对象。

具体步骤:

  1. 网页中发生了一个事件(即页面已加载或单击了某些按钮)

  2. JavaScript 创建 XMLHttpRequest 对象

  3. XMLHttpRequest 对象将请求发送到 Web 服务器

  4. 服务器处理请求

  5. 服务器将响应发送回网页

  6. 响应由 JavaScript 读取

  7. JavaScript 更新 HTML DOM

4、温习 HTTP 协议

HTTP(Hypertext Transport Protocol,超文本传输协议)详细规定了浏览器和万维网服务器之间互相通信的规则。主要包括:请求报文、响应报文。我们温习一下它们的格式与参数

4.1 请求报文

  1. 请求行:主要包括请求类型(GET、POST使用居多)、URL 路径、协议版本(一般是 HTTP/ 1.1)
  2. 请求头:主要包括 Host、Cookie、Content-Type、User-Agent,都是键值对形式
  3. 空行:空行固定
  4. 请求体:GET 类型的请求体是空的,POST 类型的请求体可以为空,也可以不为空

4.2 响应报文

  1. 响应行:主要包括协议版本(HTTP/1.1)、响应状态码(比如200、404、500)、响应字符串(比如 OK)
  2. 响应头:Content-Type(text/html;charset=utf-8)、Content-length、Content-encoding
  3. 空行:空行固定
  4. 响应体服务端返回的具体响应数据(日常开发用得最多的内容)

5、后台服务应用准备

要学习 AJAX 需要后台服务,这里我们以熟悉的 Java 为例。在企业开发中,目前大多数还是 Java 后台。这里要求对 Java 有一定的了解,并且把 JDK 环境、Maven 环境、IDEA 环境都准备好。

代码结构比较简单,如截图:

pom.xml

html 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.study</groupId>
    <artifactId>Ajax</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- SpringBoot 版本 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--web支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

application.yml (此配置非必须,如果不配置服务器端口号,默认 8080)

html 复制代码
# 配置服务器端口号
server:
  port: 8000

AjaxApp

java 复制代码
package com.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author CSDN 流放深圳
 * @description 主应用程序
 * @signature 让天下没有难写的代码
 * @create 2024-03-24 下午 4:03
 */
@SpringBootApplication
public class AjaxApp {

    public static void main(String[] args) {
        SpringApplication.run(AjaxApp.class, args);
    }
}

AjaxController

java 复制代码
package com.study.controller;

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @author CSDN 流放深圳
 * @description
 * @signature 让天下没有难写的代码
 * @create 2024-03-24 下午 4:05
 */
@RestController
public class AjaxController {

    /**
     * Get 请求
     * @return
     */
    @GetMapping("/helloWorld")
    public String helloWorld(){
        String result = "Hello,Ajax!欢迎来到新的学习基地!";
        return result;
    }

    /**
     * Post 请求
     * @return
     */
    @PostMapping("/say")
    public String say(){
        String result = "纸上得来终觉浅,绝知此事要躬行!";
        return result;
    }

    /**
     * 根据 id 获取信息
     * @param id
     * @return
     */
    @GetMapping("/getById")
    public Map<String, String> getById(@RequestParam Integer id) {
        Map<String, String> map = new HashMap<>();
        if(id == 1){
            map.put("userName", "刘德华");
            map.put("introduction", "我是1号选手,我来自香港");
        }else if(id == 2){
            map.put("userName", "周杰伦");
            map.put("introduction", "我是2号选手,我来自台湾");
            try{
                Thread.sleep(3000);//模拟超时,3秒后响应接口
            }catch (Exception e){
                System.out.println("3秒后响应接口请求");
            }
        }
        return map;
    }

    /**
     * 根据 userName 获取数据
     * @param userName
     * @return
     */
    @PostMapping("/getByName")
    public Map<String, String> getByName(@RequestParam String userName) {
        Map<String, String> map = new HashMap<>();
        if("周星驰".equals(userName)){
            map.put("userName", "周星驰");
            map.put("introduction", "曾经有一份真挚的爱情摆在我面前,我没有珍惜,等我失去的时候我才后悔莫及,人世间最痛苦的事莫过于此。如果上天能够给我一个再来一次的机会,我会对那个女孩子说三个字:我爱你。如果非要在这份爱上加上一个期限,我希望是一万年。");
        }else if("海明威".equals(userName)){
            map.put("userName", "海明威");
            map.put("introduction", "海的爱太深,时间太浅");
        }
        return map;
    }

}

CorsConfig:注意这个配置类在前期学习必须要加上,否则会有跨域问题!

java 复制代码
package com.study.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author CSDN 流放深圳
 * @description 跨域处理配置类
 * @signature 让天下没有难写的代码
 * @create 2024-03-24 下午 4:15
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    /**
     * 允许跨域配置
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//匹配所有的请求
                .allowedOrigins("*")//允许访问的客户端所有的域名
                .allowedMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS")//允许列出的请求方式(其实就是标准的 HTTP 协议的请求方式)
                .allowCredentials(true)//允许请求带有验证信息
                .maxAge(3600)
                .allowedHeaders("*");//允许客户端所有的请求头
    }
}

如果不加这个跨域处理配置类,会出现如下错误:

Access to XMLHttpRequest at 'http://127.0.0.1:8000/helloWorld' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

OK,后台基础环境搭建完毕。启动后台服务,测试:http://127.0.0.1:8000/helloWorld

6、JavaScript 原生 AJAX

6.1 AJAX 入门

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript 原生 AJAX</title>
    <script>
        window.onload = function(){//文档加载完成后执行
            
            //获取文本域元素
            let textarea = document.getElementById("textarea");

            //需求1:点击按钮1,通过 GET 请求获取数据
            let btn1 = document.getElementById("btn1");

            //绑定按钮1事件
            btn1.onclick = function(){
                //1.创建 XMLHttpRequest 对象
                let xhr = new XMLHttpRequest();
                //2.初始化,设置请求方式和 url
                xhr.open("GET", "http://127.0.0.1:8000/helloWorld");
                //设置请求头信息(可以设置自定义请求头信息)
                //xhr.setRequestHeader("Content-Type","text/plain;charset=UTF-8");
                xhr.setRequestHeader("token","token123");//自定义会话的 token

                //3.发送请求
                xhr.send();
                //4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4){
                        //判断响应状态码:一般200开头的都是成功
                        /*
                        xhr.status:响应状态码
                        xhr.statusText:状态字符串
                        xhr.getAllResponseHeaders():所有响应头
                        xhr.response:响应体
                        */
                        if(xhr.status >= 200 && xhr.status < 300){
                            //设置 textarea 的文本
                            textarea.innerHTML = xhr.response;
                        }
                    }
                }
            }

            //需求2:点击按钮2,通过 POST 请求获取数据
            let btn2 = document.getElementById("btn2");
            btn2.onclick = function(){
                let xhr = new XMLHttpRequest();
                xhr.open("POST", "http://127.0.0.1:8000/say");
                xhr.send();
                xhr.onreadystatechange = function(){
                    if(xhr.status >= 200 && xhr.status < 300){
                            //设置 textarea 的文本
                            textarea.innerHTML = xhr.response;
                        }
                }
            }

        }
    </script>
</head>
<body>
    <button id="btn1">按钮1 通过 GET 请求获取数据</button>
    <button id="btn2">按钮2 通过 POST 请求获取数据</button>
    <br><br>
    <textarea id="textarea" cols="30" rows="10"></textarea>
</body>
</html>

效果:分别点击2个按钮,在文本框看到后台服务器返回的数据。

另外,我们设置的自定义请求头信息,也可以在浏览器中看到:

关于:XMLHttpRequest 对象的 readyState 状态枚举值:

XMLHttpRequest.readyState - Web API 接口参考 | MDN

状态 描述
0 UNSENT 代理被创建,但尚未调用 open() 方法。
1 OPENED open() 方法已经被调用。
2 HEADERS_RECEIVED send() 方法已经被调用,并且头部和状态已经可获得。
3 LOADING 下载中;responseText 属性已经包含部分数据。
4 DONE 下载操作已完成或者已经失败。

6.2 处理 JSON 数据

JSON 格式的数据,已经成为企业级开发首选、必选的数据格式之一。因此掌握 JSON 格式的数据极其重要。

处理 JSON 格式有2种方式:

方式1:

  1. 设置 XMLHttpRequest 对象的返回体数据类型为 json(xhr.responseType = 'json';)
  2. 自动转换。如:const userName = xhr.response.userName;

方式2:手动对数据进行处理

let serverData = JSON.parse(xhr.response);

const userName = serverData.userName;

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>处理JSON数据</title>
    <script>
        window.onload = function(){//文档加载完成后执行
            
            //获取文本域元素
            let textarea = document.getElementById("textarea");

            //需求1:点击按钮1,通过 GET 请求获取数据
            let btn1 = document.getElementById("btn1");

            //绑定按钮1事件
            btn1.onclick = function(){
                //1.创建 XMLHttpRequest 对象
                let xhr = new XMLHttpRequest();
                //设置响应体数据的类型
                xhr.responseType = 'json';
                //2.初始化,设置请求方式和 url
                xhr.open("GET", "http://127.0.0.1:8000/getById?id=1");

                //3.发送请求
                xhr.send();
                //4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4){
                        if(xhr.status >= 200 && xhr.status < 300){
                            //方式1:手动对数据进行转换
                            /*
                            let serverData = JSON.parse(xhr.response);
                            const userName = serverData.userName;
                            const introduction = serverData.introduction;
                            textarea.innerHTML = "用户名:" + userName+",自我介绍:" + introduction;
                            */

                            // 方式2:设置响应体数据类型,然后自动转换
                            const userName = xhr.response.userName;
                            const introduction = xhr.response.introduction;
                            textarea.innerHTML = "用户名:" + userName+",自我介绍:" + introduction;
                        }
                    }
                }
            }

        }
    </script>
</head>
<body>
    <button id="btn1">GET 获取JSON数据</button>
    <br><br>
    <textarea id="textarea" cols="30" rows="10"></textarea>
</body>
</html>

效果:

6.3 解决 IE 浏览器缓存问题

问:什么是 IE 浏览器缓存问题?

答:IE 浏览器在处理 AJAX 请求时,会优先从本地判断是否有缓存,如果有,则不再向后台服务器获取数据,而是直接读取本地缓存数据。这对一些实时性要求高的场景来说,显然是不合理的。

问:如何解决 IE 浏览器缓存问题?

答:在请求头的 url 参数后面,增加一个当前时间戳的参数,传递到后台,保证每次请求都是新的请求。

如:xhr.open("GET", "http://127.0.0.1:8000/getById?id=1\&t="+new Date());

6.4 处理请求超时和网络异常

一个良好的系统,都会有超时和网络异常的处理,给用户带来更好的体验,否则让用户一直干等,显然是不合理。那么 AJAX 是如何处理请求超时和网络异常的呢?

6.4.1 处理请求超时

//设置超时时间:2秒

xhr.timeout = 2000;

//超时回调函数

xhr.ontimeout = function(){

textarea.innerHTML = "啊噢,超时了,请您去别的地方逛逛吧!";

}

6.4.2 处理网络异常

//网络异常回调

xhr.onerror = function(){

textarea.innerHTML = "Sorry!网络异常,请您稍后重试!";

}

注意代码修改了,获取的 id=2,后台有设置线程3秒后响应请求:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>处理超时和网络异常</title>
    <script>
        window.onload = function(){//文档加载完成后执行
            
            //获取文本域元素
            let textarea = document.getElementById("textarea");

            //需求1:点击按钮1,通过 GET 请求获取数据
            let btn1 = document.getElementById("btn1");

            //绑定按钮1事件
            btn1.onclick = function(){
                //1.创建 XMLHttpRequest 对象
                let xhr = new XMLHttpRequest();
                //设置响应体数据的类型
                xhr.responseType = 'json';

                //设置超时时间:2秒
                xhr.timeout = 2000;
                //超时回调函数
                xhr.ontimeout = function(){
                    textarea.innerHTML = "啊噢,超时了,请您去别的地方逛逛吧!";
                }

                //网络异常回调
                xhr.onerror = function(){
                    textarea.innerHTML = "Sorry!网络异常,请您稍后重试!";
                }

                //2.初始化,设置请求方式和 url
                xhr.open("GET", "http://127.0.0.1:8000/getById?id=2");

                //3.发送请求
                xhr.send();
                //4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4){
                        if(xhr.status >= 200 && xhr.status < 300){
                            const userName = xhr.response.userName;
                            const introduction = xhr.response.introduction;
                            textarea.innerHTML = "用户名:" + userName+",自我介绍:" + introduction;
                        }
                    }
                }
            }

        }
    </script>
</head>
<body>
    <button id="btn1">GET 获取JSON数据</button>
    <br><br>
    <textarea id="textarea" cols="30" rows="10"></textarea>
</body>
</html>

测试请求超时:

测试网络异常(把自己的浏览器的【网络】设置离线模式):

6.5 取消请求

使用 XMLHttpRequest 对象的 abort() 函数可以手动取消请求。

xhr.send();

xhr.abort();//取消请求

textarea.innerHTML = "取消了请求";

6.6 重复请求问题

问:什么是 AJAX 重复请求问题?

答:指的是同一个浏览器在某一段时间内发送了多次请求,且前几次的请求都失败了,这样会对后台服务器的压力是很大的,特别是并发量大的系统。因此需要处理重复请求的问题。

处理重复请求问题,可以按照如下步骤:

  1. 创建一个标识变量,判断是否正在发送请求。
  2. 如果判断该变量正在发送请求,则取消,并且重新创建一个新的请求。
  3. 如果请求发送成功且返回数据成功,修改标识变量的值。
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>重复请求问题</title>
    <script>
        window.onload = function(){//文档加载完成后执行
            
            //获取文本域元素
            let textarea = document.getElementById("textarea");

            //需求1:点击按钮1,通过 GET 请求获取数据
            let btn1 = document.getElementById("btn1");

            //创建标识变量
            let isSending = false;

            //绑定按钮1事件
            btn1.onclick = function(){
                //1.创建 XMLHttpRequest 对象
                let xhr = new XMLHttpRequest();

                //判断标识变量
                if(isSending){
                    xhr.abort();//取消请求
                } else {
                    isSending = true;//设置为:正在请求
                }

                //设置响应体数据的类型
                xhr.responseType = 'json';

                //设置超时时间:5秒
                xhr.timeout = 5000;
                //超时回调函数
                xhr.ontimeout = function() {
                    textarea.innerHTML = "啊噢,超时了,请您去别的地方逛逛吧!";
                }

                //网络异常回调
                xhr.onerror = function(){
                    textarea.innerHTML = "Sorry!网络异常,请您稍后重试!";
                }

                //2.初始化,设置请求方式和 url
                xhr.open("GET", "http://127.0.0.1:8000/getById?id=2");

                //3.发送请求
                xhr.send();
                //4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4){
                        //返回数据成功后,修改变量表示
                        isSending = false;
                        if(xhr.status >= 200 && xhr.status < 300){
                            const userName = xhr.response.userName;
                            const introduction = xhr.response.introduction;
                            textarea.innerHTML = "用户名:" + userName+",自我介绍:" + introduction;
                        }
                    }
                }
            }

        }
    </script>
</head>
<body>
    <button id="btn1">GET 获取JSON数据</button>
    <br><br>
    <textarea id="textarea" cols="30" rows="10"></textarea>
</body>
</html>

效果:多次快速点击发送请求,后台服务器只接收到了第一个请求并返回数据,其它请求均被取消。

7、jQuery 发送 AJAX 请求

jQuery 发送 AJAX 请求主要有 3 个常用方法

  1. $.get()
  2. $.post()
  3. $.ajax()

其中,.get() 和 .post() 都是简单的请求方式,有 4 个参数:url,[data],[callback],[type]

url:请求的 url 地址

data:参数值(键值对格式)

callback:成功时的回调函数

type:返回的数据格式,xml, html, script, json, text, _default

$.ajax() 用于处理比较复杂的请求,它可以设置很多参数,比如:头部信息(headers,注意值不允许包含中文或者说是全角输入法的内容)、成功的回调(success)、失败的回调(error)、超时设置(timeout)等

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jQuery 发送 AJAX</title>
    <!-- 通过 CDN 引入 js库 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>
    <script>
        $(function () {//文档加载完成后执行
            //发送 get 请求
            $("#btn1").click(function () {
                //get 请求有4个参数:url、参数(键值对)、回调函数、返回的数据类型
                $.get("http://127.0.0.1:8000/getById", { id: 1 }, function (data) {
                    const userName = data.userName;
                    const introduction = data.introduction;
                    //在文本域中显示信息
                    $("#textarea").val("用户名:" + userName + ",自我介绍:" + introduction);
                }, "json");
            });

            //发送 post 请求
            $("#btn2").click(function () {
                //post 请求也有4个参数:url、参数(键值对)、回调函数、返回的数据类型
                $.post("http://127.0.0.1:8000/getByName", { "userName": "周星驰" }, function (data) {
                    const userName = data.userName;
                    const introduction = data.introduction;
                    //在文本域中显示信息
                    $("#textarea").val("用户名:" + userName + ",自我介绍:" + introduction);
                }, "json");
            });

            //发送 ajax 请求。语法:jQuery.ajax(url,[settings])
            $("#btn3").click(function () {
                const myToken = "your token message";
                const otherHeader = "can no be Chinese!";
                $.ajax({
                    //url 地址
                    url: "http://127.0.0.1:8000/getByName",
                    //请求类型
                    type: "POST",
                    //头部信息(值不允许包含中文)
                    headers: {
                        token: myToken,
                        other: otherHeader
                    },
                    //参数
                    data: { "userName": "海明威" },
                    //响应体结果
                    dataType: "json",
                    //成功的回调
                    success: function (data) {
                        const userName = data.userName;
                        const introduction = data.introduction;
                        //在文本域中显示信息
                        $("#textarea").val("用户名:" + userName + ",自我介绍:" + introduction);
                    },
                    //失败的回调
                    error: function () {
                        $("#textarea").val("接口调用失败啦!");
                    },
                    //超时时间设置,单位:毫秒
                    timeout: 2000,
                })
            });

        })
    </script>
</head>

<body>
    <h2>jQuery 发送 AJAX 请求</h2>
    <button id="btn1">GET 请求</button>
    <button id="btn2">POST 请求</button>
    <button id="btn3">通用型方法 ajax</button>
    <br><br>
    <textarea id="textarea" cols="30" rows="10"></textarea>
</body>

</html>

效果:

8、axios 发送 AJAX 请求

axios 是现在前端比较流行和热门的 AJAX 工具库,也是 VUE 和 React 推荐的发送 AJAX 请求的工具包。关于 axios 后续博客会继续迭代。

axios 发送 AJAX 请求主要有 3 个常用方法(与 jQuery 类似):

  1. axios.get()
  2. axios.post()
  3. axios()

axios 在调用 AJAX 有一个 then() 函数用来接收返回数据

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>axios 发送 AJAX</title>
    <!-- 通过 CDN 引入 js库 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.js"></script>
    <script>
        window.onload = function () {//文档加载完成后执行
            //获取文本域元素
            let textarea = document.getElementById("textarea");
            //获取按钮
            let btn1 = document.getElementById("btn1");
            let btn2 = document.getElementById("btn2");
            let btn3 = document.getElementById("btn3");

            //配置 axios 基础请求路径
            axios.defaults.baseURL = "http://127.0.0.1:8000";

            //发送 get 请求
            btn1.onclick = function () {
                axios.get("/getById",
                    //参数
                    {
                        params: {
                            id: 1
                        },
                        //请求头信息
                        headers: {
                            token: "your token"
                        }
                    }
                ).then(response => {
                    //响应状态码
                    console.log(response.status);
                    //响应状态字符串
                    console.log(response.statusText);
                    //响应头信息
                    console.log(response.headers);
                    //响应体
                    console.log(response.data);
                    //在文本域显示信息
                    const userName = response.data.userName;
                    const introduction = response.data.introduction;
                    textarea.innerHTML = "用户名:" + userName + ",自我介绍:" + introduction;
                }).catch(function (error) {
                    console.log(error);
                });
            };


            //发送 post 请求
            btn2.onclick = function () {
                axios.post("/getByName",
                    //请求体参数
                    {
                        userName: "周星驰"
                    },
                    //其它参数
                    {
                        params: {
                            otherParam: "other"
                        },
                        //请求头参数
                        headers: {
                            //注意:后台服务该 POST 方法获取 userName 参数的格式是:@RequestParam String userName,需要增加头部 Content-Type 的配置
                            "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
                            "token": "your token"
                        }
                    }
                ).then(function (response) {
                    //响应状态码
                    console.log(response.status);
                    //响应状态字符串
                    console.log(response.statusText);
                    //响应头信息
                    console.log(response.headers);
                    //响应体
                    console.log(response.data);
                    //在文本域显示信息
                    const userName = response.data.userName;
                    const introduction = response.data.introduction;
                    textarea.innerHTML = "用户名:" + userName + ",自我介绍:" + introduction;
                }).catch(function (error) {
                    console.log(error);
                });
            };


            //发送 axios 请求
            btn3.onclick = function () {
                axios({
                    //请求方法
                    method: "POST",
                    //url
                    url: "/getByName",
                    //url参数
                    params: {
                        otherParam: "other"
                    },
                    //头信息
                    headers: {
                        //注意:后台服务该 POST 方法获取 userName 参数的格式是:@RequestParam String userName,需要增加头部 Content-Type 的配置
                        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
                        "token": "your token"
                    },
                    //请求体参数
                    data: {
                        userName: "海明威"
                    }
                }).then(response => {
                    //响应状态码
                    console.log(response.status);
                    //响应状态字符串
                    console.log(response.statusText);
                    //响应头信息
                    console.log(response.headers);
                    //响应体
                    console.log(response.data);
                    //在文本域显示信息
                    const userName = response.data.userName;
                    const introduction = response.data.introduction;
                    textarea.innerHTML = "用户名:" + userName + ",自我介绍:" + introduction;
                }).catch(function (error) {
                    console.log(error);
                });
            }
        }
    </script>
</head>

<body>
    <h2>axios 发送 AJAX 请求</h2>
    <button id="btn1">GET 请求</button>
    <button id="btn2">POST 请求</button>
    <button id="btn3">通用型方法 axios</button>
    <br><br>
    <textarea id="textarea" cols="30" rows="10"></textarea>
</body>

</html>

需要注意的是,后台服务接收 POST 请求使用的方式是:

复制代码
@RequestParam String userName

因此需要在 POST 请求的头部增加以下信息:

"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"

否则会报错:

AxiosError {message: 'Request failed with status code 400', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {...}, request: XMLHttpRequest, ...}

效果:

9、跨域以及处理办法

9.1 什么是跨域?

当一个请求 url 的协议、域名、端口号三者之间任意一个与当前页面 url 不同即为跨域。

跨域也叫未被了"同源策略",何为"同源",即:协议、域名、端口号 必须完全相同。

9.2 如何解决跨域问题?

9.2.1 前端解决方案 ------ JSONP

1、JSONP 是什么?

JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。

2、JSONP 怎么工作的?

在网页有一些标签天生具有跨域能力,比如:img、link、iframe、script。JSONP 就是利用 script 标签的跨域能力来发送请求的。

3、JSONP 的使用

1、动态的创建一个 script 标签,如:

javascript 复制代码
var script = document.createElement("script");

2、设置 script 的 src,设置回调函数,如:

javascript 复制代码
script.src = "http://localhost:8000/yourAPI?callback=abc";
function abc(data) {
	alert(data.name);
};

3、将 script 添加到 body 中

javascript 复制代码
document.body.appendChild(script);

4、服务器中路由的处理

javascript 复制代码
router.get("/yourAPI" , function (req , res) {
	console.log("收到请求");
	var callback = req.query.callback;
	var obj = {name:"马云", world:"让天下没有难做的生意"}
	res.send(callback+"("+JSON.stringify(obj)+")");
});

9.2.2 后台服务解决方案 ------ 跨源资源共享(CORS)

9.2.2.1 CORS 是什么?

CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 和其它标准的 HTTP 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

官网:跨源资源共享(CORS) - HTTP | MDN

9.2.2.2 CORS 怎么工作的?

CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

92.2.3 CORS 的使用

方式1:如果后台服务是 Java 语言写的,可以加上这个配置类(全局生效

java 复制代码
package com.study.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author CSDN 流放深圳
 * @description 跨域处理配置类
 * @signature 让天下没有难写的代码
 * @create 2024-03-24 下午 4:15
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    /**
     * 允许跨域配置
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//匹配所有的请求
                .allowedOrigins("*")//允许访问的客户端所有的域名
                .allowedMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS")//允许列出的请求方式(其实就是标准的 HTTP 协议的请求方式)
                .allowCredentials(true)//允许请求带有验证信息
                .maxAge(3600)
                .allowedHeaders("*");//允许客户端所有的请求头
    }
}

方式2:增加自定义过滤器(全局生效

java 复制代码
package com.study.config;

import org.springframework.context.annotation.Configuration;

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

/**
 * @author CSDN 流放深圳
 * @description 自定义跨域过滤器
 * @create 2024-03-24 下午 6:15
 * @since 1.0.0
 */
@Configuration
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, HEAD, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        response.setHeader("Access-Control-Expose-Headers", "content-disposition");

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

方式3:在某个 Controller 类或者方法上加注解 @CrossOrigin

比如:

如果在类上加这个注解,表示这个类所有的方法都允许跨域;如果只在某个方法上加这个注解,表示只有该方法允许跨域。

相关推荐
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte6 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc