系列学习前端之第 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

<?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)

# 配置服务器端口号
server:
  port: 8000

AjaxApp

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

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:注意这个配置类在前期学习必须要加上,否则会有跨域问题!

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 入门

<!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

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

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;

<!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秒后响应请求: 

<!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. 如果请求发送成功且返回数据成功,修改标识变量的值。
<!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)等

<!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() 函数用来接收返回数据

<!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 标签,如:

var script = document.createElement("script");

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

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

3、将 script 添加到 body 中

document.body.appendChild(script);

4、服务器中路由的处理

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 语言写的,可以加上这个配置类(全局生效

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:增加自定义过滤器(全局生效

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

比如:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/491059.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

flutter 弹窗之系列一

自定义不受Navigator影响的弹窗 class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;overrideState<MyHomePage> createState() > _MyHomePageState(); }class _MyHomePageState extends State<MyH…

rabbitmq-c 程序实现客户端服务端

安装mq https://blog.csdn.net/zl_momomo/article/details/82986368 需要安裝rabbitmq-server 开启rabbitmq服务 systemctl start rabbitmq-server systemctl enable rabbitmq-server. 客户端 amqp_sendstring.c include <stdint.h> #include <stdio.h> #incl…

访问二维数组本质

先从一维数组讲起 int main() {int arr[5] { 1,2,3,4,5 };for (int i 0; i < 5; i) {printf("%d",arr[i]); //对数组进行访问}return 0; } 其实 arr [ i ] * (arr i) 这两个是完全相等的&#xff0c;在c语言指针&#xff08;1&#xff09;8.数组名与 …

STM32F103 CubeMX 使用USB生成键盘设备

STM32F103 CubeMX 使用USB生成键盘设备 基础信息HID8个数组各自的功能 生成代码代码编写添加申明信息main 函数编写HID 修改1. 修改报文描述符2 修改 "usbd_hid.h" 中的申明文件 基础信息 软件版本&#xff1a; stm32cubmx&#xff1a;6.2 keil 5 硬件&#xff1a;…

Redis中的事件(三)

时间事件 事件的调度与执行 因为服务器中同时存在文件事件和时间事件两种事件类型&#xff0c;所以服务器必须对这两种事件进行调度&#xff0c;决定何时应该处理文件事件&#xff0c;何时有应该处理时间事件&#xff0c;以及花多少事件来处理它们等等。事件的调度和执行由ae…

uniApp中使用小程序XR-Frame创建3D场景(2)加载模型

上篇文章讲述了如何将XR-Frame作为子组件集成到uniApp中使用&#xff0c;只完成了简单的环境搭建&#xff0c;这篇文章讲解如何加载3D模型。 1 加入模型加载标签 在XR-Frame框架中&#xff0c;加载资源都是在wxml文件的标签中实现的。下面是wxml中完整的代码 index.wxml &l…

(二)Eureka服务搭建,服务注册,服务发现

1.Eureka注册中心 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 存在几个问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址和端口&#xff1f;有多个user-service实例地址&#xff0c;…

手机和键盘的数字键盘排序为什么是不同的?

不知道你有没有注意有一个问题。我们的手机输入法中的数字键盘&#xff0c;电脑上通用的数字键盘&#xff0c;计算器上的数字键盘等排序是不同的&#xff0c;从观察者角度看&#xff0c;0-9的数字排列有从上到下的排列&#xff0c;还有从下到上的排列。为什么会出现不同的排列方…

HWOD:句子逆序

一、题目 描述 将一个英文语句以单词为单位逆序排放。例如I am a boy逆序排放后为boy a am I。所有单词之间用一个空格隔开。语句中除了英文字母外&#xff0c;不再包含其他字符。 数据范围 输入的字符串长度满足 1<n<1000 输入 输入一个英文语句&#xff0c;每个…

【电力监控保护】AM5SE-IS防孤岛保护装置/35kV、10kV、380V分布式光伏并网供电/什么是孤岛效应/孤岛效应的危害

什么是孤岛效应&#xff01;&#xff01;&#xff01; 安科瑞薛瑶瑶18701709087 在电力系统中&#xff0c;孤岛效应指的是当电网突然断电时&#xff0c;并网光伏发电系统仍然保持对电网中部分线路的供电状态。这种情况下&#xff0c;这些线路与其他电网断开&#xff0c;形成了…

HarmonyOS页面布局方式

Column&Row组件的使用 1 概述 一个丰富的页面需要很多组件组成&#xff0c;那么&#xff0c;我们如何才能让这些组件有条不紊地在页面上布局呢&#xff1f;这就需要借助容器组件来实现。 容器组件是一种比较特殊的组件&#xff0c;它可以包含其他的组件&#xff0c;而且…

亚马逊美国站CPC认证婴儿门栏和围栏安全标准cpsc办理

美国ASTM F1004-19认证属于婴幼儿门栏和围栏安全标准 ASTM F1004-19婴幼儿门栏和围栏安全标准 2020年7月6日&#xff0c;美国消费品安全委员会发布了最终法规16 CFR 1239&#xff0c;为婴幼儿门栏和围栏建立了安全标准。该法规合并及修订了最新版本的ASTM F1004-19《婴幼儿扩展…

【分布式】——降级熔断限流

降级&熔断&限流 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记仓库&#x1f449;https://github.com/A-BigTree/tree-learning-notes 个人主页&#x1f449;https://www.abigtree.top ⭐⭐⭐⭐⭐⭐ 如果可以&#xff0c;麻烦各位看官顺手点…

API网关-Apisix路由配置教程(数据编辑器方式)

文章目录 前言一、端口修改1. apisix 端口修改2. dashboard 端口修改3. 登录密码修改 二、常用插件介绍1. 常用转换插件1.1 proxy-rewrite插件1.1.1 属性字段1.1.2 配置示例 2. 常用认证插件2.1 key-auth插件2.1.1 消费者端字段2.1.2 路由端字段2.1.3 配置示例 2.2 basic-auth插…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记11:数字电位器MCP4017

系列文章目录 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记01&#xff1a;赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记02&#xff1a;开发环境安装 嵌入式|蓝桥杯STM32G431&#xff08;…

高速行者,5G工业路由器助力车联网无缝通信

随着5G技术的飞速发展&#xff0c;智能制造正迎来一个全新的时代。5G工业路由器作为车联网的核心设备&#xff0c;正在发挥着关键的作用。它不仅提供高速稳定的网络连接&#xff0c;还支持大规模设备连接和高密度数据传输&#xff0c;为车辆之间的实时通信和信息交换提供了强有…

GitLab更新失败(CentOS)

使用yum更新GitLab&#xff0c;出现如下错误提示&#xff1a; Error: Failed to download metadata for repo gitlab_gitlab-ce: repomd.xml GPG signature verification error: Bad GPG signature 编写如下脚本&#xff1a; for pubring in /var/cache/dnf/gitlab_gitlab-?…

java Web会议信息管理系统 用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 jsp 会议信息管理系统是一套完善的web设计系统&#xff0c;对理解JSP java SERLVET mvc编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&am…

react native

简介 React Native 就是使用React和应用平台的原生功能来构建 Android 和 iOS 应用的开源框架。在 Android 和 iOS 开发中&#xff0c;一个视图是 UI 的基本组成部分&#xff0c;React 组件通过 JavaScript 来调用这些视图。可以构建自己的 Native Components(原生组件)&#…

智慧体育场馆的优势都有哪些?

体育场馆作为体育产业和事业发展的重要载体&#xff0c;全民对健康和运动的需求越来越大&#xff0c;体育馆的需求也更大。而以前的体育场馆管理不仅人工成本高&#xff0c;人民的使用和消费也不方便。因此智慧体育馆的出现大大降低了运营人力成本及现金管理风险&#xff0c;大…