Ajax 详解
Ajax 详解
前言
想象一下,你正刷着微博,手指轻轻一滑,新的动态便自动加载出来,页面没有闪白、没有跳转,仿佛内容本就静静躺在那里等你发现。又或者,你在淘宝搜索框输入“卫衣”,还没按下回车,相关推荐已经悄然浮现——这一切,都离不开一个改变了网页交互方式的技术:Ajax。
在 Ajax 出现之前,网页就像一本只能翻页的书:想看下一页?必须整页翻过去。哪怕只是改一个字,浏览器也得重新加载整个页面,用户体验如同在石器时代拨号上网。直到 2005 年,Ajax 被正式命名并广泛推广,网页才真正“活”了过来。
Ajax 并不是一门新语言,也不是某种神秘的黑科技,它更像是一位“幕后协调员”,让 JavaScript 能在不打扰用户的情况下,悄悄与服务器对话,只拿需要的数据,只更新需要的部分。从此,网页从“静态文档”进化为“动态应用”。
一、Ajax 概述
1.1 什么是 Ajax?
Ajax,全称 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML),是一种用于创建快速、动态、交互式网页应用的技术组合。它并不是一门新的编程语言,也不是一个独立的框架,而是一种利用现有 Web 技术协同工作的编程范式。
其核心思想是:在不重新加载整个页面的前提下,与服务器进行异步通信,并局部更新网页内容。
这意味着,用户在浏览网页时,可以无需等待页面刷新,就能获取新数据、提交表单、加载更多内容,从而获得接近原生应用的流畅体验。
1.2 传统网页 vs Ajax 网页
对比项 | 传统网页交互 | 使用 Ajax 的网页交互 |
---|---|---|
页面刷新 | 每次操作都需重新加载整个页面 | 仅局部更新,无需整页刷新 |
用户体验 | 中断感强,等待时间长 | 流畅自然,响应迅速 |
数据传输 | 传输整个 HTML 页面 | 仅传输必要数据(如 JSON、XML) |
服务器压力 | 较高,频繁生成完整页面 | 较低,只处理数据请求 |
技术实现 | 表单提交 + 页面跳转 | JavaScript 发起异步请求 + DOM 更新 |
举个例子:
在一个传统的博客网站中,用户提交评论后,页面会跳转或刷新,重新加载整个文章页。
而使用 Ajax 后,评论数据通过后台悄悄发送,提交成功后,新评论直接“冒”在评论区下方,用户甚至不需要离开当前阅读位置。
1.3 Ajax 的核心技术组成
Ajax 并非单一技术,而是多种 Web 技术协同工作的结果:
- HTML/XHTML:用于构建页面结构;
- CSS:用于控制页面样式与布局;
- JavaScript:核心驱动语言,负责发起请求、处理响应;
- DOM(Document Object Model):JavaScript 通过操作 DOM 实现局部内容更新;
- XmlHttpRequest 或 Fetch API:与服务器进行异步通信的关键对象;
- XML / JSON:早期常用 XML 传输数据,如今 JSON 已成为主流数据格式。
虽然 Ajax 名字中包含 XML,但现代开发中,JSON(JavaScript Object Notation)因其轻量、易解析的特性,已成为 Ajax 数据交换的首选格式。
1.4 Ajax 的优势
- 提升用户体验:无刷新更新,操作更流畅;
- 减少服务器负载:只请求数据,而非整个页面;
- 降低带宽消耗:传输数据量小,节省流量;
- 支持异步操作:用户可继续操作页面,无需等待响应;
- 前后端分离友好:便于构建 RESTful API 和单页应用(SPA)。
1.5 Ajax 的局限与注意事项
- 依赖 JavaScript:用户需启用 JS,否则功能失效;
- 对 SEO 不够友好:早期搜索引擎难以抓取动态内容(现已有改善方案);
- 调试相对复杂:异步逻辑可能增加调试难度;
- 跨域限制:受同源策略约束,需通过 CORS 或代理解决;
- 历史管理问题:早期 SPA 需手动管理浏览器历史记录(现可通过 History API 解决)。
二、Ajax原理
2.1 Ajax 入门案例
在学习原理之前,先写一个入门案例,走一个流程,方便后面的理解。
我使用的是Java,那我的服务端写一个HelloServlet。
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("hello ajax");}
}
写一个html界面,方便展示。
界面中写了一个button,用来进行事件绑定,写一个span,用来展示发送Ajax请求后,服务端返回的信息。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Hello Ajax</title><script>function getMessage() {//实例化一个XmlHttpRequestvar XmlHttpRequest = new XmlHttpRequest();//设置XmlHttpRequest对象的回调函数XmlHttpRequest.onreadystatechange = function () {if (XmlHttpRequest.readyState === 4 && XmlHttpRequest.status === 200) {var p = document.getElementById("message");p.innerHTML = XmlHttpRequest.responseText;p.style.color = "red";}};//设置发送请求的方式和请求的资源路径XmlHttpRequest.open("GET","/JavaWeb_07/hello");//发送请求XmlHttpRequest.send();}</script>
</head>
<body>
<button onclick="getMessage()">按钮</button>
<span id="message"></span>
</body>
</html>
可以看到,我们button按钮绑定了getMessage()方法。
在getMessage()中,
1.实例化一个XmlHttpRequest对象
var XmlHttpRequest = new XmlHttpRequest();
2.设置XmlHttpRequest对象的回调函数
XmlHttpRequest.onreadystatechange = function () {if (XmlHttpRequest.readyState === 4 && XmlHttpRequest.status === 200) {var p = document.getElementById("message");p.innerHTML = XmlHttpRequest.responseText;p.style.color = "red";}};
3.设置发送请求的方式和请求的资源路径
XmlHttpRequest.open("GET","/JavaWeb_07/hello");
4.发送请求
XmlHttpRequest.send();
接下来我们启动服务,点击按钮进行测试。
可以发现页面不用刷新,便可以展示我们服务端返回的信息,hello ajax
可以看到我们请求的Type是xhr,这恰恰证明了我们刚刚使用了Ajax。
2.2 Ajax 原理
- 事件触发(如点击按钮或页面加载)。
- AJAX 请求:通过 JavaScript 创建一个
XmlHttpRequest
对象,向服务器发送请求。 - 服务器处理请求:服务器(通常使用 PHP、Node.js 等)接收请求,处理并返回响应数据(JSON、XML、HTML等)。
- AJAX 响应处理:浏览器接收响应,使用 JavaScript 在页面上更新内容,而无需重新加载整个页面。
2.3 XmlHttpRequest
XmlHttpRequest 是 AJAX 的基础。
2.3.1 创建 XmlHttpRequest 对象
所有现代浏览器(IE7+、Edge、Firefox、Chrome、Safari 以及 Opera)均内建 XmlHttpRequest 对象。
创建 XmlHttpRequest 对象的语法:
var xhr = new XmlHttpRequest();
2.3.2 XmlHttpRequest 请求
XmlHttpRequest 对象用于和服务器交换数据。
通过该对象,我们可以在不刷新页面的情况下,向服务器发送请求并接收响应数据,从而实现网页的局部更新。
要发起一个 HTTP 请求,主要使用 XmlHttpRequest
的两个关键方法:open()
和 send()
。
基本语法
xhr.open(method, url, async);
xhr.send(string);
open()
:设置请求的基本参数。send()
:实际发送请求到服务器。
1、open(method, url, async)
该方法用于配置请求的类型、目标地址以及是否异步执行。
参数 | 说明 |
---|---|
method | 请求方法,通常为 "GET" 或 "POST" |
url | 服务器上资源的路径(可以是 .txt 、.json 、.php 、.asp 等) |
async | 布尔值,true 表示异步,false 表示同步(推荐使用 true ) |
示例:
xhr.open("GET", "/JavaWeb_07/hello", true);
2、send(string)
该方法用于发送请求。参数仅在使用 POST
请求时需要传入要提交的数据。
参数 | 说明 |
---|---|
string | 要发送的数据字符串(仅用于 POST 请求) |
示例:
xhr.send(); // GET 请求无需数据xhr.send("name=Tom&age=25"); // POST 请求发送表单数据
3、GET VS POST
对比项 | GET | POST |
---|---|---|
用途 | 获取数据 | 提交数据 |
缓存 | 可能被缓存(需注意) | 不缓存,适合更新操作 |
数据长度 | 有 URL 长度限制 | 无限制,适合大数据 |
安全性 | 数据暴露在 URL 中 | 相对更安全(仍需 HTTPS) |
编码要求 | 特殊字符可能出错 | 更稳定,支持复杂数据 |
建议使用 POST 的场景:
- 提交表单数据(如注册、登录)
- 更新服务器数据(如修改数据库)
- 发送大量或包含敏感信息的数据
4、GET 请求示例
xhr.open("GET", "/JavaWeb_07/hello", true);
xhr.send();
注意:浏览器可能会缓存 GET 请求的结果。为避免缓存,可在 URL 后添加唯一参数(如时间戳或随机数):
xhr.open("GET", "/JavaWeb_07/hello?t=" + Math.random(), true);
xhr.send();
若需传递参数,直接拼接在 URL 中:
xhr.open("GET", "/JavaWeb_07/hello?keyword=ajax&limit=10", true);
xhr.send();
5、POST 请求示例
发送 POST 请求时,建议设置正确的请求头(Content-Type
),以模拟表单提交行为:
xhr.open("POST", "/JavaWeb_07/hello", true);// 设置 HTTP 请求头,表示发送的是表单格式数据
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");// 发送键值对形式的数据
xhr.send("fname=Henry&lname=Ford");
6、setRequestHeader(header, value)
用于设置 HTTP 请求头,常用于 POST 请求。
参数 | 说明 |
---|---|
header | 头字段名称,如 "Content-Type" |
value | 头字段值,如 "application/x-www-form-urlencoded" |
注意:
setRequestHeader()
必须在open()
之后、send()
之前调用。
7、异步处理:async = true(推荐)
Ajax 的核心优势在于“异步”。将 async
设为 true
,JavaScript 不会阻塞等待服务器响应,用户仍可操作页面。
此时,必须通过监听 onreadystatechange
事件来处理响应:
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {document.getElementById("myDiv").innerHTML = xhr.responseText;}
};
xhr.open("GET", "/JavaWeb_07/hello", true);
xhr.send();
我们将在下一节详细讲解 onreadystatechange
事件与 readyState
状态码。
8、同步请求:async = false(不推荐)
虽然可以设置 async = false
实现同步请求,但这会导致 JavaScript 阻塞,直到服务器返回响应,期间页面将无法响应用户操作。
xhr.open("GET", "ajax_info.txt", false);
xhr.send();
// JavaScript 会在这里暂停,直到响应完成
document.getElementById("myDiv").innerHTML = xhr.responseText;
❗ 警告:同步请求会导致页面“冻结”,影响用户体验,现代开发中应避免使用。
2.3.3 XmlHttpRequest 响应
当服务器接收到客户端请求后,会返回相应的数据。要获取并处理这些响应数据,我们可以通过 XmlHttpRequest
对象提供的两个核心属性:responseText
和 responseXML
。
根据服务器返回的数据格式,选择合适的属性来接收和解析响应内容,是实现动态页面更新的关键步骤。
属性 | 描述 |
---|---|
responseText | 以字符串形式返回服务器响应数据(适用于文本、HTML、JSON 等非 XML 数据) |
responseXML | 以 XML DOM 对象形式返回服务器响应数据(仅当响应为 XML 格式时有效) |
使用建议:
- 大多数现代应用使用 JSON 格式传输数据 → 推荐使用
responseText
并配合JSON.parse()
解析。- 若后端返回 XML 数据(较少见),可使用
responseXML
进行 DOM 操作。
1、responseText:获取字符串响应
responseText
是最常用的响应属性,它将服务器返回的内容作为纯字符串返回,无论内容是 HTML 片段、JSON 文本还是普通文本。
使用场景
- 加载静态文本文件(如
.txt
) - 接收 JSON 格式数据(需手动解析)
- 动态插入 HTML 内容到页面
示例:处理 JSON 响应
如果服务器返回的是 JSON 字符串(如 { "name": "Tom", "age": 25 }
),需使用 JSON.parse()
转换为 JavaScript 对象:
{ "name": "Tom", "age": 25 }
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Hello Ajax</title><script>function getMessage() {//实例化一个XMLHttpRequestvar xhr = new XMLHttpRequest();//设置xmlHttpRequest对象的回调函数//xmlHttpRequest.readyState 1 2 3 4//xmlHttpRequest.status 响应状态码,相应行状态码 200 404 500 304xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {var data = JSON.parse(xhr.responseText);document.getElementById("message").innerText = data.name + " " + data.age;}};xhr.open("GET", "/static/user.json", true);xhr.send();}</script>
</head>
<body>
<button onclick="getMessage()">按钮</button><br>
<span id="message"></span>
</body>
</html>
2、responseXML:解析 XML 响应
如果服务器返回的是 XML 格式的数据,responseXML
属性会自动将其解析为一个可用的 XML DOM 对象,你可以像操作 HTML DOM 一样遍历和提取其中的数据。
注意:只有当服务器正确设置响应头
Content-Type: application/xml
或text/xml
,且返回内容为合法 XML 时,responseXML
才有效。
示例:读取并解析 XML 文件
假设服务器返回一个名为 cd_catalog.xml
的文件,内容如下:
<CATALOG><CD><TITLE>Empire Burlesque</TITLE><ARTIST>Bob Dylan</ARTIST><COUNTRY>USA</COUNTRY></CD><CD><TITLE>Hide Your Heart</TITLE><ARTIST>Bonnie Tyler</ARTIST><COUNTRY>UK</COUNTRY></CD>
</CATALOG>
我们可以使用 responseXML
获取该 XML 对象,并提取所有艺术家名称:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Hello Ajax</title><script>function getMessage() {//实例化一个XMLHttpRequestvar xhr = new XMLHttpRequest();//设置xmlHttpRequest对象的回调函数xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {// 获取 XML DOM 对象var xmlDoc = xhr.responseXML;var txt = "";var artists = xmlDoc.getElementsByTagName("ARTIST");// 遍历所有 ARTIST 元素for (var i = 0; i < artists.length; i++) {txt += artists[i].childNodes[0].nodeValue + "<br>";}// 显示结果document.getElementById("message").innerHTML = txt;}};xhr.open("GET", "/static/cd_catalog.xml", true);xhr.send();}</script>
</head>
<body>
<button onclick="getMessage()">按钮</button><br>
<span id="message"></span>
</body>
</html>
如何选择响应类型?
响应类型 | 推荐属性 | 处理方式 |
---|---|---|
纯文本 / HTML 片段 | responseText | 直接插入 innerHTML |
JSON 数据 | responseText | 使用 JSON.parse() 转换 |
XML 数据 | responseXML | 使用 getElementsByTagName 、getAttribute 等 DOM 方法解析 |
- 优先使用 JSON
尽管 Ajax 名字中有 XML,但如今 JSON 已成为主流数据格式,更轻量、更易解析。 - 确保编码一致
服务器应正确设置字符编码(如 UTF-8),避免中文乱码。 - 检查响应状态
始终在readyState === 4
且status === 200
时才处理响应,防止错误数据被使用。 - XML 使用场景有限
XML 多用于遗留系统或特定行业标准(如 SOAP 接口),新项目建议使用 JSON。
2.3.4 XMLHttpRequest onreadystatechange 事件
在使用 XMLHttpRequest
发起异步请求后,我们无法预知服务器何时返回响应。为了在响应就绪时及时处理数据,必须依赖一个关键机制:onreadystatechange
事件。
这个事件是 Ajax 实现“异步通信”的核心,它允许我们在请求状态变化时执行特定逻辑,确保只在数据准备就绪时进行操作。
1、onreadystatechange
onreadystatechange
是一个事件处理函数,每当 XMLHttpRequest
对象的 readyState
属性发生变化时(从 0 到 4),该事件就会被触发一次。
我们可以将处理响应的逻辑写在这个事件的回调函数中,从而实现对请求生命周期的精确控制。
基本语法:
xhr.onreadystatechange = function() {// 当 readyState 改变时执行的代码
};
注意:必须在调用
send()
之前绑定onreadystatechange
,否则可能错过某些状态。
2、readyState:请求的五种状态
readyState
属性表示当前请求的生命周期状态,它是一个从 0
到 4
的整数,共五个阶段:
值 | 状态 | 说明 |
---|---|---|
0 | UNSENT | 请求未初始化,尚未调用 open() 方法 |
1 | OPENED | 已调用 open() ,服务器连接已建立 |
2 | HEADERS_RECEIVED | 已接收到响应头(send() 已调用) |
3 | LOADING | 正在接收响应体(数据下载中) |
4 | DONE | 请求完成,响应已就绪,可安全处理数据 |
只有当
readyState === 4
时,表示整个请求过程结束,才能安全地读取responseText
或responseXML
。
3、status:HTTP 响应状态码
即使请求完成(readyState === 4
),也不代表成功获取数据。我们还需要检查 status
属性来判断响应是否成功。
常见状态码:
状态码 | 含义 |
---|---|
200 | OK —— 请求成功,服务器正常返回数据 ✅ |
404 | Not Found —— 请求的资源不存在 ❌ |
500 | Internal Server Error —— 服务器内部错误 ❌ |
403 | Forbidden —— 无权限访问 ❌ |
400 | Bad Request —— 请求语法错误 ❌ |
标准用法:判断请求是否成功完成
最经典的写法是同时检查 readyState
和 status
:
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {// 请求完成且成功,处理响应document.getElementById("myDiv").innerHTML = xhr.responseText;}
};
✅ 只有当两个条件都满足时,才说明:
- 请求已完成(
readyState === 4
)- 服务器返回了有效数据(
status === 200
)
4、事件触发次数
onreadystatechange
事件在整个请求过程中会被触发 4 次以上(理论上最多 5 次,对应 0→1→2→3→4
的每次变化),例如:
xhr.onreadystatechange = function() {console.log("当前 readyState:", xhr.readyState);
};
实际开发中,我们通常只关心
readyState === 4
的情况,其他状态可用于调试或实现加载进度提示。
5、使用回调函数实现可复用的 Ajax 请求
如果你的页面中有多个 Ajax 请求,为了避免重复代码,可以封装一个通用函数,并通过回调函数(callback)传递不同的处理逻辑。
示例:封装通用 Ajax 函数
function loadXMLDoc(url, callback) {var xhr = new XMLHttpRequest();xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {// 请求成功,执行传入的回调函数callback(xhr.responseText);} else if (xhr.readyState === 4 && xhr.status !== 200) {// 请求完成但出错console.error("请求失败,状态码:", xhr.status);}};xhr.open("GET", url, true);xhr.send();
}
调用示例:
loadXMLDoc("/static/user.json", function(data) {var user = JSON.parse(data);document.getElementById("message").innerHTML = user.name;
});
💡 这种“函数传参 + 回调”的方式,是早期 JavaScript 实现异步编程的经典模式,为后续 Promise 和 async/await 奠定了基础。
三、使用Ajax
Ajax 的核心在于无需刷新页面即可与服务器通信。实现这一能力的关键是 XMLHttpRequest
对象。
1、原生Ajax
所谓“原生 Ajax”,是指不依赖 jQuery、Axios 等第三方库,直接使用浏览器提供的 XMLHttpRequest
API 发起异步请求。这是理解 Ajax 原理的基础,也是现代前端开发的必备知识
使用原生 JavaScript 发送一个完整的 Ajax 请求,并结合后端 Servlet 实现数据交互。
1.1 GET 请求
GET 请求是最常见的 Ajax 请求方式,通常用于获取数据。参数通过 URL 查询字符串(?key=value
)传递。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-GET</title>
</head>
<body><div><button id="btn">点击 hello ajax</button><span id="result"></span></div><script>var btn = document.getElementById("btn");var result = document.getElementById("result");btn.onclick = function () {var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {result.innerHTML = xhr.responseText;result.style.color = "green";}};// GET 请求:参数通过 URL 传递xhr.open("GET", "/JavaWeb_07/get?username=zhangsan");xhr.send(); // GET 请求 send() 参数为 null 或省略};</script>
</body>
</html>
@WebServlet("/get")
public class GetServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//接收参数String username = req.getParameter("username");System.out.println("用户名:" + username);//做出响应resp.getWriter().write("username = " + username);}
}
xhr.open("GET", url)
:URL 中携带查询参数。xhr.send()
:GET 请求不需发送请求体,传null
或不传参数。- 适用于获取数据,如加载新闻、用户信息等。
测试
访问http://localhost:8080/JavaWeb_07/Ajax-GET.html,点击按钮
可以看到我们的请求路径中携带参数。
1.2 POST 请求
POST 请求用于向服务器提交数据,数据包含在请求体中,安全性高于 GET,且无长度限制。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-POST</title>
</head>
<body><form id="form">用户名:<input type="text" name="username" id="username"/> <br>密码:<input type="password" name="password" id="password"/> <br><input type="submit" id="btn"/></form><div id="result" style="border: red 1px solid; width: 100px; height: 100px"></div><script>var btn = document.getElementById('btn');btn.addEventListener('click', function (e) {e.preventDefault(); // 阻止表单默认提交var username = document.getElementById('username').value;var password = document.getElementById('password').value;var result = document.getElementById('result');var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {result.innerHTML = xhr.responseText;result.style.color = 'green';}};xhr.open('POST', '/JavaWeb_07/post');// 必须设置请求头,告知服务器数据格式为表单xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');xhr.send('username=' + username + '&password=' + password);});</script>
</body>
</html>
@WebServlet("/post")
public class PostServlet extends HttpServlet{@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取参数req.setCharacterEncoding("utf-8");String username = req.getParameter("username");String password = req.getParameter("password");//处理....System.out.println("用户名:" + username);System.out.println("密码:" + password);//响应resp.setContentType("text/html;charset=utf-8");resp.getWriter().write("username = " + username + " password = " + password);}
}
setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
:必须设置,否则服务器无法解析表单数据。send(data)
:POST 请求的数据通过send()
发送。- 适用于登录、注册、提交表单等场景。
测试
访问http://localhost:8080/JavaWeb_07/Ajax-POST.html,在表单中输入用户名和密码 admin 111111,点击按钮。
可以看到请求体中携带着表单数据。
1.3 响应 JSON
现代 Web 开发中,JSON 是主流的数据交换格式。
前端可通过 JSON.parse()
手动解析,
或使用 xhr.responseType = 'json'
自动解析。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-响应JSON</title>
</head>
<body><button id="btn">获取 JSON 数据</button> <br><div id="result" style="border: red 1px solid; width: 200px; height: 500px;"></div><script>var btn = document.getElementById('btn');btn.addEventListener('click', function () {var result = document.getElementById('result');var xhr = new XMLHttpRequest();// 设置响应类型为 JSON,自动解析xhr.responseType = 'json';xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {//处理响应数据--json// result.innerHTML = xhr.responseText;//方式一:手动设置// var data = JSON.parse(xhr.responseText);// var html = '';// for (var key in data) {// console.log(key + ':' + data[key]);// html += key + ':' + data[key] + '<br>';// }// result.innerHTML = html;//方式二:设置响应体的数据类型var html ='';var data = xhr.response;for (var key in data) {console.log(key + ':' + data[key]);html += key + ':' + data[key] + '<br>';}result.innerHTML = html;}};xhr.open('GET', '/JavaWeb_07/json');xhr.send();});</script>
</body>
</html>
@WebServlet("/json")
public class JSONServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置响应格式为 JSON,编码 UTF-8resp.setContentType("application/json;charset=UTF-8");// 构造 JSON 字符串(注意:字符串中的双引号要转义)String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";// 获取输出流并写入响应resp.getWriter().write(json);}
}
resp.setContentType("application/json")
:必须设置,否则浏览器可能不识别为 JSON。xhr.responseType = 'json'
:自动将响应体解析为 JavaScript 对象。- 也可使用
JSON.parse(xhr.responseText)
手动解析。
测试
访问http://localhost:8080/JavaWeb_07/Ajax-JSON.html,点击按钮。
可以看到服务端相应的是json类型的字符串,我们通过xhr.responseType = 'json'
:自动将响应体解析为 JavaScript 对象。
1.4 IE 缓存问题
IE 浏览器会对 Ajax 请求进行缓存,导致相同 URL 的请求不会重新发送,数据无法及时更新。
xhr.open('GET', '/JavaWeb_07/json?t=' + new Date().getTime());
其他方法:设置请求头
Cache-Control: no-cache
,但添加时间戳最简单有效。
1.5 请求超时与网络问题
网络不稳定时,请求可能长时间无响应。应设置超时机制和错误处理,提升用户体验。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-超时与网络问题</title>
</head>
<body><button id="btn">发送请求</button><span id="result"></span>
</body>
<script>var btn = document.getElementById("btn");var result = document.getElementById("result");btn.onclick = function () {var xhr = new XMLHttpRequest();// 设置超时时间(毫秒)xhr.timeout = 2000;// 超时回调xhr.ontimeout = function () {alert("请求超时,请稍后重试!");};// 网络错误回调xhr.onerror = function () {alert("网络连接失败,请检查网络!");};xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {result.innerHTML = xhr.responseText;}};xhr.open("GET", "/JavaWeb_07/delay"); // 后端延迟 3 秒xhr.send();};
</script>
</html>
@WebServlet("/delay")
public class DelayServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {Thread.sleep(3000); // 模拟延迟} catch (InterruptedException e) {e.printStackTrace();}resp.getWriter().write("请求成功,但已延迟响应");}
}
xhr.timeout = 2000
:设置 2 秒超时,单位毫秒。ontimeout
:超时触发。onerror
:网络异常触发(如断网、DNS 失败)。
测试
访问http://localhost:8080/JavaWeb_07/Ajax-TimeoutAndOffline.html,点击按钮
超时回调函数触发。
将chrome浏览器做一个设置。
改成这样。
现在再次点击按钮。
网络错误回调函数触发。
1.6 取消请求
用户可能在请求未完成时切换页面或取消操作,此时应主动取消请求,避免资源浪费。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-取消请求</title>
</head>
<body><button>发送请求</button><button>取消请求</button><span id="result"></span>
</body>
<script>const btns = document.querySelectorAll("button");var result = document.getElementById("result");let xhr = null;// 发送请求btns[0].onclick = function () {xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {result.innerHTML = xhr.responseText;}};xhr.open("GET", "/JavaWeb_07/delay");xhr.send();};// 取消请求btns[1].onclick = function () {if (xhr) {xhr.abort(); // 取消请求result.innerHTML = "请求已取消";}};
</script>
</html>
xhr.abort()
:立即终止请求,onreadystatechange
仍会触发,但status
为0
。- 适用于取消上传、搜索建议等场景。
测试
访问http://localhost:8080/JavaWeb_07/Ajax-Abort.html
打开浏览器控制台,点击发送按钮后,3秒内点击取消。
点击发送按钮后,请求的Status显示pending
点击取消按钮后,请求的Status显示canceled
1.7 重复发送问题
用户快速多次点击按钮,可能触发多个重复请求,造成服务器压力或界面混乱。
解决方案:使用标识变量控制
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-防止重复发送</title>
</head>
<body><button id="btn">点击发送请求</button><span id="result"></span>
</body>
<script>var btn = document.getElementById("btn");var result = document.getElementById("result");let xhr = null;let isSending = false; // 标识是否正在发送请求btn.onclick = function () {// 如果正在发送,则取消上一次请求if (isSending) {xhr.abort();alert("上一次请求已取消");}xhr = new XMLHttpRequest();isSending = true; // 设置为正在发送xhr.onreadystatechange = function () {if (xhr.readyState === 4) {isSending = false; // 请求完成,重置标识}if (xhr.status === 200) {result.innerHTML = xhr.responseText;}};xhr.open("GET", "/JavaWeb_07/delay");xhr.send();};
</script>
</html>
isSending
变量防止并发请求。- 可选择“取消旧请求”或“忽略新请求”策略。
测试
访问http://localhost:8080/JavaWeb_07/Ajax-RepeatSend.html,快速点击请求按钮
可以看到快速请求同一个url时,只会响应最新的请求,之前的请求会被取消。
2、jQuery
jQuery 封装了原生 XMLHttpRequest
,提供了更简洁、易用的 Ajax 方法。本节介绍三种常用方式:
$.get()
—— 发送 GET 请求$.post()
—— 发送 POST 请求$.ajax()
—— 通用型异步请求方法(高度可配置)
⚠️ 注意:jQuery 已逐渐被现代前端框架(如 Vue/React)取代,但在维护老项目或快速开发中仍广泛使用。
2.1 GET 请求:$.get()
用于向服务器获取数据,参数自动拼接在 URL 中。
语法格式
$.get(url, [data], [success], [dataType]);
参数 | 说明 |
---|---|
url | 请求地址(必填) |
data | 要发送的数据对象(可选) |
success | 成功回调函数:function(data, status, xhr) |
dataType | 预期服务器返回的数据类型,如 'json' , 'text' 等 |
案例:使用 $.get()
获取 JSON 数据
$('#btn-get').click(() => {$.get('/JavaWeb_07/jquery', {a: 100,b: 200}, (data) => {console.log(data);$('#result-title').text(data.message);$('#result-content').html(`<li><strong>用户ID:</strong> ${data.userId}</li><li><strong>用户名:</strong> ${data.username}</li><li><strong>登录状态:</strong> ${data.success ? '✅ 成功' : '❌ 失败'}</li>`);$('#result').show();}, 'json');
});
说明
- 参数
{a: 100, b: 200}
会自动拼接为:/JavaWeb_07/jquery?a=100&b=200
dataType: 'json'
表示期望返回 JSON,jQuery 会自动解析为 JS 对象- 适用于加载配置、用户信息等只读操作
2.2 POST 请求:$.post()
用于向服务器提交数据,数据放在请求体中。
语法格式
$.post(url, [data], [success], [dataType]);
参数含义与 $.get()
完全相同。
案例:使用 $.post()
提交表单类数据
$('#btn-post').click(() => {$.post('/JavaWeb_07/jquery', {a: 100,b: 200}, (data) => {console.log(data);$('#result-title').text(data.message);$('#result-content').html(`<li><strong>用户ID:</strong> ${data.userId}</li><li><strong>用户名:</strong> ${data.username}</li><li><strong>登录状态:</strong> ${data.success ? '✅ 成功' : '❌ 失败'}</li>`);$('#result').show();}, 'json');
});
说明
- 数据通过请求体发送,安全性高于 GET
- jQuery 自动设置
Content-Type: application/x-www-form-urlencoded
- 适用于登录、注册、提交订单等场景
2.3 通用型方法:$.ajax()
最灵活、功能最全的 Ajax 方法,支持所有配置项。
语法格式
$.ajax({url: '', // 请求地址type: 'GET', // 请求方式:GET、POST、PUT、DELETE 等data: {}, // 发送的数据(对象或字符串)dataType: 'json', // 预期返回数据类型success: function(data) {}, // 成功回调error: function(xhr, status, err) {}, // 失败回调complete: function(xhr, status) {}, // 完成回调(无论成功失败)timeout: 3000, // 超时时间(毫秒)headers: {}, // 自定义请求头async: true, // 是否异步(默认 true)cache: false // 是否缓存 GET 请求(false 可解决 IE 缓存)
});
案例:使用 $.ajax()
发送配置化请求
$('#btn-common').click(() => {$.ajax({url: '/JavaWeb_07/jquery',type: 'GET',data: {a: 100,b: 200},dataType: 'json',success: (data) => {console.log(data);$('#result-title').text(data.message);$('#result-content').html(`<li><strong>用户ID:</strong> ${data.userId}</li><li><strong>用户名:</strong> ${data.username}</li><li><strong>登录状态:</strong> ${data.success ? '✅ 成功' : '❌ 失败'}</li>`);$('#result').show();},error: () => {console.log("请求失败!");alert("请求出错,请检查网络或服务器!");},complete: () => {console.log("请求结束(无论成功或失败)");},timeout: 2000,headers: {c: 300,d: 400}});
});
说明
type: 'GET'
可改为'POST'
实现 POST 请求headers
可用于传递 Token、版本号等自定义头信息timeout
设置超时时间,避免请求卡死complete
适合用于隐藏 loading 动画cache: false
可防止 IE 缓存(jQuery 内部会自动加时间戳)
测试
@WebServlet("/jquery")
public class JQueryServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {String a = req.getParameter("a");System.out.println("a = " + a);String b = req.getParameter("b");System.out.println("b = " + b);// 设置响应格式为 JSON,编码 UTF-8resp.setContentType("application/json;charset=UTF-8");// 构造 JSON 字符串(注意:字符串中的双引号要转义)String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";// 获取输出流并写入响应resp.getWriter().write(json);}
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-jQuery-GET-POST-Common</title><!--使用jQuery发送GET POST 的Ajax请求,通用型方法Ajax--><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div id="container" class="container" style="margin-top: 50px;"><h2>jQuery发送Ajax请求</h2><hr><button id="btn-get" class="btn btn-primary">GET</button><button id="btn-post" class="btn btn-success">POST</button><button id="btn-common" class="btn btn-info">通用型方法Ajax</button><div id="result" class="alert alert-success" style="margin-top: 20px;"><h4 id="result-title"></h4><p id="result-content"></p></div>
</div>
</body>
<script>$('button').eq(0).click(() => {$.get('/JavaWeb_07/jquery',{a: 100,b: 200},(data) => {console.log(data);$('#result-title').text(data.message);$('#result-content').html(`<li><strong>用户ID:</strong> ${data.userId}</li><li><strong>用户名:</strong> ${data.username}</li><li><strong>登录状态:</strong> ${data.success ? '✅ 成功' : '❌ 失败'}</li>
`);$('#result').show();},'json')});$('button').eq(1).click(() => {//http://localhost:8080/$.post('/JavaWeb_07/jquery',{a: 100,b: 200},(data) => {console.log(data);$('#result-title').text(data.message);$('#result-content').html(`<li><strong>用户ID:</strong> ${data.userId}</li><li><strong>用户名:</strong> ${data.username}</li><li><strong>登录状态:</strong> ${data.success ? '✅ 成功' : '❌ 失败'}</li>
`);$('#result').show();},'json')});$('button').eq(2).click(() => {$.ajax({//urlurl: '/JavaWeb_07/jquery',//参数data:{a: 100,b: 200},//请求方式type: 'GET',//响应数据类型dataType: 'json',//成功回调success: (data) => {console.log(data);$('#result-title').text(data.message);$('#result-content').html(`<li><strong>用户ID:</strong> ${data.userId}</li><li><strong>用户名:</strong> ${data.username}</li><li><strong>登录状态:</strong> ${data.success ? '✅ 成功' : '❌ 失败'}</li>
`);$('#result').show();},//超时时间timeout: 2000,//失败回调error: () => {console.log("出错了!!");},//完成回调complete: () => {console.log("请求结束!!");},//头信息headers: {c:300,d:400}});});
</script>
</html>
访问http://localhost:8080/JavaWeb_07/Ajax-jQuery-GET-POST-Common.html
点击GET,发送GET请求。
可以看到我们的url已经携带了我们设置的参数。
我们设置的dataType: 'json'
,jQuery自动帮我们在请求头上加上Accept: application/json
点击POST,发送POST请求。
可以看到,我们要携带的数据会自动加到请求体中。
点击通用型方法,发送GET请求。
可以看到我们在请求头中设置的信息。
3、Axios
Axios
是一个基于 Promise 的 HTTP 客户端,常用于浏览器和 Node.js 中发送异步请求。相比 jQuery 的 $.ajax
,Axios 更现代、轻量、语义清晰,且原生支持 Promise 风格(.then().catch()
),是目前主流前端框架(Vue/React)推荐使用的网络请求库。
Axios 的三大特点
- 基于 Promise:支持
.then()
和.catch()
,避免回调地狱 - 自动转换 JSON:请求/响应数据自动序列化/反序列化
- 拦截器支持:可统一处理请求头、错误、loading 等
- 浏览器兼容性好:支持所有现代浏览器
3.1 GET 请求:axios.get()
用于向服务器获取数据,参数通过 params
选项传递,会自动拼接到 URL 上。
语法格式
axios.get(url, {params: { /* URL 查询参数 */ },headers: { /* 请求头 */ }
})
.then(response => { /* 成功回调 */ })
.catch(error => { /* 失败回调 */ });
示例:使用 axios.get()
获取数据
btns[0].addEventListener("click", function () {axios.get('/JavaWeb_07/axios', {params: {a: 12345,b: 67890},headers: {"Content-Type": "application/json"}}).then(function (response) {console.log(response);result_title.textContent = response.data.message + 'get' || "操作成功";result_content.innerHTML = `状态: ${response.data.success ? '✅ 成功' : '❌ 失败'}<br>用户ID: ${response.data?.userId || '未知'}<br>用户名: ${response.data?.username || '未知'}`;}).catch(function (error) {console.log(error);});
});
说明
params
中的数据会自动拼接为:/JavaWeb_07/axios?a=12345&b=67890
headers
可自定义请求头(如认证 Token)Content-Type: application/json
在 GET 请求中通常不必要(GET 没有请求体),但 Axios 允许设置- 响应数据在
response.data
中(不是response
本身!)
⚠️ 注意:
response
对象结构如下:{data: { success: true, message: "登录成功", ... }, // 服务器返回的 JSONstatus: 200,statusText: "OK",headers: { ... },config: { ... } }
3.2 POST 请求:axios.post()
用于向服务器提交数据,数据放在请求体中,通过 data
选项传递。
语法格式
axios.post(url, data, {params: { /* URL 参数 */ },headers: { /* 请求头 */ }
})
.then(response => { /* 成功 */ })
.catch(error => { /* 失败 */ });
示例:使用 axios.post()
提交登录数据
btns[1].addEventListener("click", function () {axios.post('/JavaWeb_07/axios', {username: "admin",password: "123456"}, {params: {a: 12345,b: 67890},headers: {"Content-Type": "application/json","height": "180cm","weight": "80kg"}}).then(function (response) {console.log(response);result_title.textContent = response.data.message + 'post' || "操作成功";result_content.innerHTML = `状态: ${response.data.success ? '✅ 成功' : '❌ 失败'}<br>用户ID: ${response.data?.userId || '未知'}<br>用户名: ${response.data?.username || '未知'}`;});
});
说明
- 第二个参数
{username, password}
是请求体(Request Body) params
仍可附加 URL 参数headers
设置了自定义头(如身高体重),可用于调试或权限控制Content-Type: application/json
表示发送的是 JSON 数据,后端需用getReader()
或InputStream
读取
3.3 通用型方法:axios(config)
最灵活的方式,支持所有配置项,适合复杂请求。
语法格式
axios({method: 'GET' | 'POST' | 'PUT' | 'DELETE',url: '/api/xxx',params: { /* URL 参数 */ },data: { /* 请求体数据 */ },headers: { /* 请求头 */ },timeout: 5000
})
.then(response => { ... })
.catch(error => { ... });
示例:使用通用 axios()
发送 POST 请求
btns[2].addEventListener("click", function () {axios({method: "POST",url: '/JavaWeb_07/axios',params: {a: 12345,b: 67890},headers: {"height": "180cm","weight": "80kg"},data: {username: "admin",password: "123456"}}).then(response => {console.log(response);result_title.textContent = response.data.message + 'get' || "操作成功";result_content.innerHTML = `状态: ${response.data.success ? '✅ 成功' : '❌ 失败'}<br>用户ID: ${response.data?.userId || '未知'}<br>用户名: ${response.data?.username || '未知'}`;});
});
说明
- 所有配置集中在一个对象中,结构清晰
data
是请求体,params
是 URL 参数headers
可传递自定义信息method
明确指定请求方式
测试
@WebServlet("/axios")
public class AxiosServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {String a = req.getParameter("a");System.out.println("a = " + a);String b = req.getParameter("b");System.out.println("b = " + b);// 设置响应格式为 JSON,编码 UTF-8resp.setContentType("application/json;charset=UTF-8");// 构造 JSON 字符串(注意:字符串中的双引号要转义)String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";// 获取输出流并写入响应resp.getWriter().write(json);}
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-Axios</title><!--Axios--><script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
</head>
<body>
<div><div id="container" class="container" style="margin-top: 50px;"><h2>Axios发送Ajax请求</h2><hr><button id="btn-get" class="btn btn-primary">GET</button><button id="btn-post" class="btn btn-success">POST</button><button id="btn-common" class="btn btn-info">通用型方法Ajax</button><div id="result" class="alert alert-success" style="margin-top: 20px;"><h4 id="result-title"></h4><p id="result-content"></p></div></div>
</div>
</body>
<script>var btns = document.querySelectorAll("button");var result_title = document.getElementById("result-title");var result_content = document.getElementById("result-content");btns[0].addEventListener("click", function () {axios.get('/JavaWeb_07/axios', {//url参数信息params: {a: 12345,b: 67890},//请求头信息headers: {"Content-Type": "application/json"}}).then(function (response) {console.log(response);// 设置标题(比如显示 message)result_title.textContent = response.data.message + 'get' || "操作成功";// 设置内容(可以拼接多个字段)result_content.innerHTML = `状态: ${response.data.success ? '✅ 成功' : '❌ 失败'}<br>用户ID: ${response.data?.userId || '未知'}<br>用户名: ${response.data?.username || '未知'}`;}).catch(function (error) {console.log(error);}).finally(function () {// always executed});})btns[1].addEventListener("click", function () {axios.post('/JavaWeb_07/axios', {username: "admin", //请求体password: "123456"}, {//url 参数params: {a: 12345,b: 67890},//请求头参数headers: {"Content-Type": "application/json","height": "180cm","weight": "80kg"},}).then(function (response) {console.log(response);// 设置标题(比如显示 message)result_title.textContent = response.data.message + 'post' || "操作成功";// 设置内容(可以拼接多个字段)result_content.innerHTML = `状态: ${response.data.success ? '✅ 成功' : '❌ 失败'}<br>用户ID: ${response.data?.userId || '未知'}<br>用户名: ${response.data?.username || '未知'}`;})})btns[2].addEventListener("click", function () {axios({//请求方法method: "POST",//urlurl: '/JavaWeb_07/axios',//url参数params: {a: 12345,b: 67890},//请求头headers: {"height": "180cm","weight": "80kg"},//请求体data: {username: "admin",password: "123456"}}).then(response => {console.log(response);//响应状态码response.status;//响应状态字符串response.statusText;//响应头信息response.headers;//响应数据response.data;// 设置标题(比如显示 message)result_title.textContent = response.data.message + 'get' || "操作成功";// 设置内容(可以拼接多个字段)result_content.innerHTML = `状态: ${response.data.success ? '✅ 成功' : '❌ 失败'}<br>用户ID: ${response.data?.userId || '未知'}<br>用户名: ${response.data?.username || '未知'}`;})})
</script>
</html>
访问http://localhost:8080/JavaWeb_07/Ajax-Axios.html
点击GET,发送GET请求。params
中的数据会自动拼接为:/JavaWeb_07/axios?a=12345&b=67890
响应数据在 response.data
中(不是 response
本身!)
点击POST,发送POST请求。第二个参数 {username, password}
是请求体(Request Body)params
仍可附加 URL 参数
点击通用型方法,发送POST请求。
data
是请求体,params
是 URL 参数
4、Fetch
fetch()
是现代浏览器原生提供的 全局函数,用于发起网络请求。它基于 Promise,语法简洁,无需引入第三方库(如 jQuery 或 Axios),是当前 Web 开发中推荐使用的原生异步请求方式。
支持情况:所有现代浏览器(IE 不支持,需 polyfill)
Fetch 的核心特点
特性 | 说明 |
---|---|
原生支持 | 浏览器内置,无需引入库 |
基于 Promise | 支持 .then().catch() 链式调用 |
分步处理响应 | 需手动调用 .json() 、.text() 等解析数据 |
灵活配置 | 支持自定义 method 、headers 、body 等 |
不自动判断 HTTP 状态 | 即使返回 404/500,fetch 也不会自动 reject(需手动判断) |
4.1 GET 请求:fetch(url, options)
用于从服务器获取数据,参数通过 URL 传递。
语法格式
fetch(url, {method: 'GET',headers: { /* 请求头 */ }
})
.then(response => response.json()) // 解析 JSON 响应
.then(data => { /* 使用数据 */ })
.catch(error => { /* 处理错误 */ });
示例:使用 fetch
发送 GET 请求
btns[0].addEventListener("click", function () {fetch('/JavaWeb_07/fetch', {method: 'GET',headers: {'Content-Type': 'application/json','height': '180','weight': '80'}}).then(response => {return response.json(); // 将响应体转为 JSON 对象}).then(data => {result_title.innerHTML = data.message;result_content.innerHTML = `状态: ${data.success ? '成功' : '失败'} <br>用户ID: ${data?.userId || '未知'} <br>用户名: ${data?.username || '未知'}`;});
});
说明
method: 'GET'
可省略(默认就是 GET)headers
中的Content-Type
在 GET 请求中通常无意义(GET 没有请求体),但可用于传递自定义信息response.json()
返回一个 Promise,必须等待它解析完成后才能拿到真正的数据- URL 参数需手动拼接:
/JavaWeb_07/fetch?a=123&b=456
如需传递参数,可改写为:
fetch('/JavaWeb_07/fetch?a=12345&b=67890', { method: 'GET', ... })
4.2 POST 请求:fetch()
提交数据
用于向服务器提交数据,数据通过 body
选项发送。
语法格式
fetch(url, {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded' // 或 application/json},body: 'key=value&key2=value2' // 表单格式 或 JSON 字符串
})
.then(response => response.json())
.then(data => { /* 成功 */ })
.catch(error => { /* 失败 */ });
示例:使用 fetch
发送 POST 请求
btns[1].addEventListener("click", function () {fetch('/JavaWeb_07/fetch', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded','height': '180','weight': '80'},body: 'username=admin&password=123456' // 字符串形式的表单数据}).then(response => response.json()).then(data => {result_title.innerHTML = data.message;result_content.innerHTML = `状态: ${data.success ? '成功' : '失败'} <br>用户ID: ${data?.userId || '未知'} <br>用户名: ${data?.username || '未知'}`;});
});
测试
@WebServlet("/fetch")
public class FetchServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 获取 URL 参数(GET 或 POST 的 ?a=1&b=2)String a = req.getParameter("a");String b = req.getParameter("b");System.out.println("a = " + a);System.out.println("b = " + b);// 设置响应格式resp.setContentType("application/json;charset=UTF-8");// 返回 JSON 响应String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";resp.getWriter().write(json);}
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-Fetch</title><!--Fetch(全局函数)--><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
</head>
<body>
<div><div id="container" class="container" style="margin-top: 50px;"><h2>Fetch发送Ajax请求</h2><hr><button id="btn-get" class="btn btn-primary">GET</button><button id="btn-post" class="btn btn-success">POST</button><div id="result" class="alert alert-success" style="margin-top: 20px;"><h4 id="result-title"></h4><p id="result-content"></p></div></div>
</div>
</body>
<script>var btns = document.querySelectorAll("button");var result_title = document.getElementById("result-title");var result_content = document.getElementById("result-content");btns[0].addEventListener("click", function () {fetch('/JavaWeb_07/fetch', {//请求方法method: 'GET',//请求头headers: {'Content-Type': 'application/json','height': '180','weight': '80'},}).then(response => {return response.json();}).then(data => {result_title.innerHTML = data.message;result_content.innerHTML = `状态: ${data.success ? '成功' : '失败'} <br>用户ID: ${data?.userId || '未知'} <br>用户名: ${data?.username || '未知'}`;});});btns[1].addEventListener("click", function () {fetch('/JavaWeb_07/fetch', {//请求方法method: 'POST',//请求头headers: {'Content-Type': 'application/json','height': '180','weight': '80'},//请求体body: 'username=admin&password=123456'}).then(response => {return response.json();}).then(data => {result_title.innerHTML = data.message;result_content.innerHTML = `状态: ${data.success ? '成功' : '失败'} <br>用户ID: ${data?.userId || '未知'} <br>用户名: ${data?.username || '未知'}`;});});
</script>
</html>
访问http://localhost:8080/JavaWeb_07/Ajax-Fetch.html
4.3 Fetch 响应处理详解
fetch
的 response
对象提供多种方法解析响应体:
方法 | 说明 |
---|---|
response.json() | 解析为 JSON 对象(最常用) |
response.text() | 解析为字符串 |
response.blob() | 解析为二进制 Blob(如图片) |
response.formData() | 解析为 FormData |
response.arrayBuffer() | 解析为 ArrayBuffer |
必须调用其中之一,否则无法获取数据!
4.4 错误处理注意事项
fetch
只有在网络错误时才会进入 .catch()
,HTTP 状态码如 404、500 不会触发 catch!
手动检查 response.ok
fetch('/JavaWeb_07/fetch').then(response => {if (!response.ok) {throw new Error(`HTTP ${response.status}: ${response.statusText}`);}return response.json();}).then(data => {// 处理数据}).catch(error => {console.error('请求失败:', error);});
四、跨域问题
在现代 Web 开发中,前端页面与后端 API 通常部署在不同的服务器上(如:前端 http://localhost:3000
,后端 http://localhost:8080
),此时浏览器会因 同源策略 阻止请求,这就是“跨域问题”。
1、同源
同源 指两个 URL 的以下三个部分完全相同:
组成部分 | 示例 |
---|---|
协议(Protocol) | http:// 或 https:// |
域名(Host) | localhost 或 api.example.com |
端口(Port) | :8080 或 :3000 (默认 80/443 可省略) |
同源示例:
- 页面:
http://localhost:3000/index.html
- 请求:
http://localhost:3000/api/user
同源
跨域示例:
页面地址 | 请求地址 | 原因 |
---|---|---|
http://localhost:3000 | http://localhost:8080/api | 端口不同 |
http://localhost:3000 | https://localhost:3000/api | 协议不同 |
http://a.com | http://b.com/api | 域名不同 |
🔐 同源策略(Same-Origin Policy):浏览器的安全机制,阻止 AJAX 请求跨域,但允许
<script>
、<img>
、<link>
等标签跨域加载资源。
2、跨域
当请求的 协议、域名、端口 任一不同,即为跨域。
跨域的典型错误
在浏览器控制台中,你会看到类似错误:
Access to fetch at 'http://localhost:8080/api' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这表示:浏览器阻止了该请求,因为后端没有明确允许跨域访问。
解决跨域的常见方案
方案 | 说明 | 是否推荐 |
---|---|---|
CORS(跨域资源共享) | 后端设置响应头允许跨域 | 推荐(现代标准) |
JSONP | 利用 <script> 标签可跨域的特性 | 仅支持 GET,已过时 |
代理服务器 | 前端或 Nginx 代理请求 | 推荐(开发/生产通用) |
WebSocket | 全双工通信,不受同源限制 | 特定场景 |
3、jsonp
3.1 JSONP 原理
JSONP(JSON with Padding) 是一种“野路子”解决跨域的方法,利用了 <script>
标签不受同源策略限制的特性。
工作原理
-
前端定义一个 回调函数(如
handle(data)
) -
动态创建
<script src="http://xxx/api?callback=handle">
标签 -
后端接收到请求后,返回:
handle({"success": true, "message": "登录成功"});
-
浏览器加载该 JS 脚本,自动执行
handle(...)
函数 -
前端在
handle
中处理数据
优点:兼容老浏览器(IE6+)
缺点:仅支持 GET 请求,安全性差(易受 XSS 攻击),已逐渐被淘汰
示例:JSONP 手动实现(原生 JS)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-Jsonp原理</title>
</head>
<body>
<div><div id="result" style="width: 300px;height: 200px;border: blueviolet 1px solid"></div>
</div><!-- 定义回调函数 -->
<script>
function handle(data){var result = document.getElementById('result');result.innerHTML = data.success + " " + data.message+ " " + data.userId + " " + data.username;
}
</script><!-- 动态加载跨域脚本 -->
<script src="http://localhost:8080/JavaWeb_07/jsonp"></script>
</body>
</html>
后端 JsonpServlet
代码
@WebServlet("/jsonp")
public class JsonpServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 设置响应类型resp.setContentType("application/json;charset=UTF-8");// 构造 JSON 数据String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";// 返回:handle({ ... })resp.getWriter().write("handle(" + json + ")");}
}
关键点说明
步骤 | 说明 |
---|---|
handle(...) | 必须与前端定义的函数名一致 |
script 标签加载 | 浏览器自动执行返回的 JS 代码 |
数据传递 | 通过函数调用传参 |
3.2 jQuery 发送 JSONP 请求
jQuery 提供了更简洁的 JSONP 支持,自动处理回调函数名。
示例:使用 $.getJSON()
发送 JSONP
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-Jsonp原理</title><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div><button id="btn">点击发送jsonp请求</button><div id="result" style="width: 300px;height: 200px;border: blueviolet 1px solid"></div>
</div>
</body>
<script>
$('#btn').click(function () {$.getJSON("http://localhost:8080/JavaWeb_07/jquery-jsonp?callback=?", function (data) {$('#result').html(`success:${data.success},message: ${data.message},userId: ${data.userId},username: ${data.username}`);});
});
</script>
</html>
后端 jQueryJsonpServlet
(自动识别回调名)
@WebServlet("/jquery-jsonp")
public class jQueryJsonpServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 获取 jQuery 自动生成的回调函数名,如:jQuery1234567890123_1234567890String callback = req.getParameter("callback");resp.setContentType("application/javascript;charset=UTF-8");String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";// 返回:callbackName({"data": ...})resp.getWriter().write(callback + "(" + json + ")");}
}
说明
callback=?
:jQuery 会自动替换?
为一个唯一的函数名(如jQuery123...
)- 后端通过
req.getParameter("callback")
获取该函数名 - 返回
callbackName({...})
,jQuery 自动执行并调用你的回调函数
3.3 JSONP 的局限性
问题 | 说明 |
---|---|
仅支持 GET 请求 | 无法发送 POST、PUT、DELETE |
安全风险 | 易受 XSS 攻击,返回的 JS 可能被篡改 |
无法捕获网络错误 | <script> 加载失败不会触发 catch |
回调函数污染全局 | 需手动清理,或使用一次性函数 |
不支持现代特性 | 如进度条、超时控制、拦截器等 |
4、CORS
CORS(Cross-Origin Resource Sharing,跨域资源共享)是现代浏览器支持的一种标准跨域解决方案。它通过在服务器端设置特定的响应头,告诉浏览器允许来自指定源的跨域请求。
现代开发应优先使用 CORS,由后端设置响应头:
// 在 Servlet 中添加:
resp.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有域名
// resp.setHeader("Access-Control-Allow-Origin", "http://localhost:3000"); // 指定域名
resp.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
resp.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
✅ 优点:支持所有 HTTP 方法,安全可控,是 W3C 标准
4.1 工作原理
- 前端发起 AJAX 请求
- 浏览器自动在请求中添加
Origin
头 - 服务器检查
Origin
是否在允许列表中 - 如果允许,服务器返回带有 CORS 头的响应
- 浏览器收到响应后,根据 CORS 头决定是否将数据暴露给前端 JavaScript
4.2 核心响应头
响应头 | 作用 | 示例 |
---|---|---|
Access-Control-Allow-Origin | 允许哪些源访问 | http://localhost:63342 或 * |
Access-Control-Allow-Methods | 允许哪些 HTTP 方法 | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers | 允许客户端发送的请求头 | Content-Type, Authorization |
Access-Control-Allow-Credentials | 是否允许携带凭据(如 Cookie) | true |
Access-Control-Max-Age | 预检请求缓存时间(秒) | 3600 |
Access-Control-Expose-Headers | 允许客户端访问的响应头 | Content-Type, X-Custom-Header |
注意事项
Access-Control-Allow-Origin: *
与Access-Control-Allow-Credentials: true
不能同时使用- 如果需要携带 Cookie,
Origin
必须是具体域名,不能是*
- 如果需要携带 Cookie,
- 简单请求无需预检,复杂请求会先发送
OPTIONS
预检请求 - 前端若需携带 Cookie,必须设置
xhr.withCredentials = true
测试
@WebServlet("/CORS")
public class CORSServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {//设置响应头,解决跨域//允许哪些源访问resp.setHeader("Access-Control-Allow-Origin", "*");//允许哪些请求方式访问resp.setHeader("Access-Control-Allow-Methods", "*");//请求结果缓存时间resp.setHeader("Access-Control-Max-Age", "3600");//允许哪些请求头访问resp.setHeader("Access-Control-Allow-Headers", "*");//允许携带验证信息 比如cookieresp.setHeader("Access-Control-Allow-Credentials", "false");//允许哪些头暴露resp.setHeader("Access-Control-Expose-Headers", "*");// 设置响应格式为 JSON,编码 UTF-8resp.setContentType("application/json;charset=UTF-8");// 构造 JSON 字符串(注意:字符串中的双引号要转义)String json = "{\"success\": true, \"message\": \"登录成功\", \"userId\": 1001, \"username\": \"zhangsan\"}";// 获取输出流并写入响应resp.getWriter().write(json);}
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Ajax-CORS</title><!--CORS 解决跨域-->
</head>
<body>
<div><button>点击请求ajax</button><span id="result"></span>
</div>
</body>
<script>const btns = document.querySelectorAll("button");var result = document.getElementById("result");let xhr = null;btns[0].onclick = function () {//实例化一个XMLHttpRequest对象xhr = new XMLHttpRequest();//设置XMLHttpRequest对象的回调函数xhr.onreadystatechange = () => {if (xhr.readyState == 4 && xhr.status == 200) {result.innerHTML = xhr.responseText;result.style.color = "green";}};//设置发送请求的方式和请求的资源路径xhr.open("GET", "http://localhost:8080/JavaWeb_07/CORS");//发送请求xhr.send();}
</script>
</html>
总结
Ajax(Asynchronous JavaScript and XML) 是一种前端核心技术,它通过 XMLHttpRequest
或现代 Fetch API
实现页面与服务器的异步通信,无需刷新整个页面即可局部更新内容,显著提升用户体验,广泛应用于动态加载、表单提交、搜索建议等场景。
核心流程:
事件触发 → 创建 XMLHttpRequest
→ 发送请求(GET/POST)→ 服务器处理 → 返回数据(JSON/XML)→ 回调函数处理响应 → DOM 局部更新。
跨域问题:浏览器基于同源策略(协议、域名、端口必须一致),默认禁止 AJAX 请求跨域资源,以防止恶意攻击。
主要解决方案:
- CORS(Cross-Origin Resource Sharing,推荐)
- 服务器通过设置响应头(如
Access-Control-Allow-Origin
)主动授权跨域访问。 - 支持所有 HTTP 方法(GET、POST 等),可携带 Cookie(需
withCredentials
配合)。 - 复杂请求会先发送
OPTIONS
预检。 - 现代开发的标准方案,安全、灵活、功能完整。
- 服务器通过设置响应头(如
- JSONP(JSON with Padding,历史方案)
- 利用
<script>
标签不受同源策略限制的特性,通过动态创建 script 标签加载数据,并用回调函数处理。 - 仅支持 GET 请求,安全性较低,调试困难。
- 已被 CORS 取代,仅用于兼容老系统。
- 利用