当前位置: 首页 > news >正文

系列学习前端之第 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*/@Overridepublic 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.初始化,设置请求方式和 urlxhr.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、4xhr.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.初始化,设置请求方式和 urlxhr.open("GET", "http://127.0.0.1:8000/getById?id=1");//3.发送请求xhr.send();//4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4xhr.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.初始化,设置请求方式和 urlxhr.open("GET", "http://127.0.0.1:8000/getById?id=2");//3.发送请求xhr.send();//4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4xhr.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.初始化,设置请求方式和 urlxhr.open("GET", "http://127.0.0.1:8000/getById?id=2");//3.发送请求xhr.send();//4.事件绑定,处理服务器返回的结果。readyState 是 xhr 对象中的属性,表示状态有:0、1、2、3、4xhr.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",//urlurl: "/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*/@Overridepublic 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 {@Overridepublic 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);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void destroy() {Filter.super.destroy();}
}

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

比如:

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

相关文章:

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

1、AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff08;中文名&#xff1a;阿贾克斯&#xff09;&#xff0c;就是异步的 JS 和 XML。AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组合在一起使用的新方式。AJAX 可以在浏览器中向服务器发送异步请求…...

iOS - Runtime - Class的结构

文章目录 iOS - Runtime - Class的结构前言1. Class的结构1.1 Class的结构1.1.1 objc_class1.1.2 class_rw_t1.1.3 class_ro_t 1.2 class_rw_t和class_ro_t的区别1.3 class_rw_t和class_ro_t的关系1.3.1 分析关系1.3.2 原因 1.4 method_t1.4.1 Type Encoding1.4.2 types iOS - …...

MySQL高阶语句(一)

一、常用查询 &#xff08;增、删、改、查&#xff09; 对 MySQL 数据库的查询&#xff0c;除了基本的查询外&#xff0c;有时候需要对查询的结果集进行处理。 例如只取 10 条数据、对查询结果进行排序或分组等等 1、按关键字排序 PS:类比于windows 任务管理器 使用 SELECT 语…...

MySQL知识总结

一条 SQL 语句过来的流程是什么样的&#xff1f; ①当客户端连接到 MySQL 服务器时&#xff0c;服务器对其进行认证。可以通过用户名与密码认证&#xff0c;也可以通过 SSL 证书进行认证。登录认证后&#xff0c;服务器还会验证客户端是否有执行某个查询的操作权限。 ②在正式…...

Go-Gin-Example 第八部分 优化配置接口+图片上传功能

文章目录 前情提要本节目标 优化配置结构讲解落实修改配置文件优化配置读取及设置初始化顺序第一步 验证 抽离file 实现上传图片接口图片名加密封装image的处理逻辑编写上传图片的业务逻辑增加图片上传的路由 验证实现前端访问 http.FileServerr.StaticFS修改文章接口新增、更新…...

阿里云国际DDoS高防的定制场景策略

DDoS高防的定制场景策略允许您在特定的业务突增时段&#xff08;例如新业务上线、双11大促销等&#xff09;选择应用独立于通用防护策略的定制防护策略模板&#xff0c;保证适应业务需求的防护效果。您可以根据需要设置定制场景策略。 背景信息 定制场景策略提供基于业务场景…...

v4l2采集视频

Video4Linux2&#xff08;v4l2&#xff09;是用于Linux系统的视频设备驱动框架&#xff0c;它允许用户空间应用程序直接与视频设备&#xff08;如摄像头、视频采集卡等&#xff09;进行交互。 linux系统下一切皆文件&#xff0c;对视频设备的操作就像对文件的操作一样&#xff…...

Spring Cloud 八:微服务架构中的数据管理

Spring Cloud 一&#xff1a;Spring Cloud 简介 Spring Cloud 二&#xff1a;核心组件解析 Spring Cloud 三&#xff1a;API网关深入探索与实战应用 Spring Cloud 四&#xff1a;微服务治理与安全 Spring Cloud 五&#xff1a;Spring Cloud与持续集成/持续部署&#xff08;CI/C…...

Chrome/Edge 使用 Markdown Viewer 查看 Markdown 格式文件

Chrome/Edge 使用 Markdown Viewer 查看 Markdown 格式文件 0. 引言1. 安装 Markdown Viewer 插件2. 使用 Markdown Viewer 阅读 Markdown 格式文件 0. 引言 大部分程序员都喜欢 Markdown 格式的文件&#xff0c;这时给一些没有在电脑上安装 Markdown 编辑器的同事分享资料时&…...

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…...

【Flink实战】Flink hint更灵活、更细粒度的设置Flink sql行为与简化hive连接器参数设置

文章目录 一. create table hints1. 语法2. 示例3. 注意 二. 实战&#xff1a;简化hive连接器参数设置三. select hints(ing) SQL 提示&#xff08;SQL Hints&#xff09;是和 SQL 语句一起使用来改变执行计划的。本章介绍如何使用 SQL 提示来实现各种干预。 SQL 提示一般可以…...

【python从入门到精通】-- 第二战:注释和有关量的解释

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…...

【手写AI代码目录】准备发布的教程

文章目录 1. tensorboard2. F.cross_entropy(input_tensor, target) F.log_softmax() F.nll_loss() 1. tensorboard from torch.utils.tensorboard import SummaryWriter# TensorBoard writer SummaryWriter(runs/mnist_experiment_1) ...if i % 100 99: # 每 100 个 b…...

2024.3.9|第十五届蓝桥杯模拟赛(第三期)

2024.3.9|十五届蓝桥杯模拟赛&#xff08;第三期&#xff09; 第一题 第二题 第三题 第四题 第五题 第六题 第七题 第八题 第九题 第十题 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&…...

搭建PHP本地开发环境:看这一篇就够了

什么是PHP本地开发环境 PHP本地开发环境是指在个人计算机上模拟的服务器环境&#xff0c;这使得开发者能够在没有网络连接的情况下也能开发、测试和调试PHP应用程序。就像在你的电脑里装个小“服务器”&#xff0c;即使没网也能搞定PHP程序的开发和修修补补。这就是PHP本地开发…...

[蓝桥杯 2015]机器人数目

机器人数目 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 少年宫新近邮购了小机器人配件&#xff0c;共有3类。 A 类含有&#xff1a;8个轮子&#xff0c;1个传感器&#xff1b; B 类含有: 6个轮子&#xff0…...

Codeforces Round 935 (Div. 3)

A. Setting up Camp&#xff08;模拟&#xff09; #include<iostream> #include<algorithm> using namespace std; const int N 2e5 10;int main(){int t, n;scanf("%d", &t);int a, b, c;while(t--){scanf("%d%d%d", &a, &b, …...

自然语言处理下载nltk模块库

nltk安装 目录 nltk安装 1.官方下载 2.离线下载 2.1 下载nltk资料包 2.2 解压下载的资料包重命名 2.2.1 将解压后的packages文件夹重命名为nltk_data 2.2.2 查看将重命名的文件夹放在那个位置 2.2.3 将上述nltk_data 文件夹放在 2.2.2 打印的位置处 3.验证是否下载成…...

题解:CF1937B(Binary Path)

题解&#xff1a;CF1937B&#xff08;Binary Path&#xff09; 一、 理解题意 1. 题目链接 CodeForces&#xff1b; 洛谷。 2. 题目翻译 给定一个 由 0 0 0 和 1 1 1 组成的 2 2 2 行 n n n 列的网格上寻找一条路径&#xff0c;使得这条路径上所有的数串联起来形成的0…...

JS——9大陷阱

一、警惕A>X>B写法 3>2>1 返回值为false&#xff08;原因&#xff1a;3>2为true&#xff0c;会默认转成数字1&#xff0c;1>1为false&#xff09; 1<4<3 返回值为true&#xff08;原因&#xff1a;1<4为true&#xff0c;会默认转成数字1&#xff…...

USB - 通过configfs配置Linux USB Gadget

Linux USB gadget configured through configfs Overview USB Linux 小工具是一种具有 UDC&#xff08;USB 设备控制器&#xff09;的设备&#xff0c;可连接到 USB 主机&#xff0c;以扩展其附加功能&#xff0c;如串行端口或大容量存储功能。 A USB Linux Gadget is a device…...

迷宫与陷阱(蓝桥杯)

文章目录 迷宫与陷阱问题描述bfs解题思路代码 迷宫与陷阱 问题描述 小明在玩一款迷宫游戏&#xff0c;在游戏中他要控制自己的角色离开一间由 N x N 个格子组成的2D迷宫。 小明的起始位置在左上角&#xff0c;他需要到达右下角的格子才能离开迷宫&#xff0c;每一步&#xf…...

Temple of Doom靶场nodejs获取shellss-manager漏洞tcpdump提权

下载链接&#xff1a; Temple of Doom: 1 ~ VulnHub 下载完成后直接在vxbox中导入即可&#xff0c;网络链接模式根据自身情况而定&#xff08;我采用的桥接模式&#xff09; 正文&#xff1a; 先用nmap进行扫描靶机ip nmap -sn 192.168.1.1/24 对192.168.1.5进行端口探测&a…...

day03_mysql_课后练习 - 参考答案

文章目录 day03_mysql_课后练习mysql练习题第1题第2题第3题第4题第5题 day03_mysql_课后练习 mysql练习题 第1题 案例&#xff1a; 1、创建一个数据库&#xff1a;day03_test01_school 2、创建如下表格 表1 Department表的定义 字段名字段描述数据类型主键外键非空唯一D…...

creator-webview与Android交互

title: creator-webview与Android交互 categories: Cocos2dx tags: [cocos2dx, creator, webview, 交互] date: 2024-03-23 13:17:20 comments: false mathjax: true toc: true creator-webview与Android交互 前篇 Android&#xff1a;你要的WebView与 JS 交互方式 都在这里了…...

22.WEB渗透测试-BurpSuite(一)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;21.WEB渗透测试-HTTP协议&#xff08;下&#xff09;-CSDN博客 工具的使用需要先搭建靶场…...

前端性能优化:防抖与节流

一、防抖和节流主要是干什么的 防抖和节流主要用于控制函数执行的频率&#xff0c;通过限制函数的触发次数&#xff0c;避免函数被过度调用而引发的性能问题或产生不必要的副作用。 二、防抖 防抖是什么: 1、对于在事件被触发 n 秒后再执行的回调 --> 延迟执行 2、如果…...

Copilot 编程助手的介绍及使用

介绍 Copilot 是2021年由 GitHub 与 OpenAI 合作研发的一款编程助手&#xff0c;同时也是全球首款使用OpenAI Codex模型&#xff08;GPT-3后代&#xff09;打造的大规模生成式AI开发工具。 Copilot 底层模型目前经过了数十亿行公开代码的训练&#xff0c;与大多数代码辅助工具…...

数据库专题(oracle基础和进阶)

前言 本专题主要记录自己最近学的数据库&#xff0c;有兴趣一起补习的可以一起看看&#xff0c;有补充和不足之处请多多指出。希望专题可以给自己还有读者带去一点点提高。 数据库基本概念 本模块有参考&#xff1a;数据库基本概念-CSDN博客 数据库管理系统是一个由互相关联的…...

web蓝桥杯2022省赛真题:水果拼盘

代码及注释&#xff1a; /* TODO&#xff1a;待补充代码 */ #pond {display: flex; //flex布局flex-direction: column; //主轴方向从上到下flex-wrap: wrap; //子元素换行 } 知识点&#xff1a; flex弹性布局 父元素&#xff1a;diasplay: flex; flex-d…...