AJAX (Asynchronous Javascript And Xml)
ajax意为"异步的Javascript和Xml"
ajax不是一个技术,而是多个技术结合的产物
传统的请求,如url地址栏回车,form表单提交,js代码,超链接的方式发送的请求有什么缺点?
- 收到服务器响应后页面会全部刷新,用户体验差
- 传统请求导致用户的体验有空白期,体验不连贯
AJAX不但可以做到不刷新页面展示新页面,还可以在浏览器中发送异步请求,请求A和B是异步的,谁也不需要等谁,类似于多线程并发,从现在起,学了ajax后尽可能使用ajax发送请求
对于AJAX来说,服务器(WEB Server端)可能会给WEB前端响应回来三种数据:可能是普通文本,可能是XML字符串,可能是JSON字符串
想在浏览器发送ajax请求,这些代码都是js语法的代码,其实发送ajax请求,就是要编写js代码,必须用前端浏览器里的XMLHttpRequest对象发送ajax请求
XMLHttpRequest
Ajax请求的发送和接收服务器的响应,完全靠XMLHttpRequest对象完成
-
XMLHttpRequest对象中的readyState属性记录下了XMLHttpRequest对象的状态
readyState属性值
0 请求未初始化 1 服务器连接已建立 2 请求已收到 3 正在处理情求 4 请求已完成且响应已就绪
属性的值变成4的时候,表示这个AJAX请求以及响应己经全部完成了
-
XMLHttpRequest对象中的onreadystatechange属性用来定义当上面的readyState属性值发生变化时被调用的函数
-
通过XMLHttpRequest对象的responseText属性来获取响应的信息
下面写一个简单的ajaxGET请求
服务器端代码该咋写还咋写,只不过返回的响应内容在浏览器端被XMLHttpRequest对象接收到了而已
package com.study.ajax;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/request01")
public class AjaxServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.print("nb666");
}
}
前端代码,当判断XMLHttpRequest对象的状态是4且HTTP状态是200成功时,就收内容,用XMLHttpRequest对象的responseText属性接收文本形式的响应内容放到div,可以看到页面没有刷新,但内容更新了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax get请求</title>
</head>
<body>
<script type="text/javascript">
window.onload = function () {
document.getElementById("btn").onclick = function () {
// 发送ajax的get请求,一共四步
// 1. 创建XMLHttpRequest对象
var xhr = new XMLHttpRequest()
// 2. 注册回调函数
xhr.onreadystatechange = function () {
// readyState状态码为4表示xhr对象请求已完成且响应已就绪
if (this.readyState == 4) {
// status是HTTP状态码,注意和上面区分
if (this.status == 404) {
alert("访问的资源不存在")
} else if (this.status == 500) {
alert("服务器错误")
} else if (this.status == 200) {
console.log("响应成功")
// 将收到的结果放的div中
document.getElementById("myDiv").innerHTML = this.responseText
}
}
}
// 3. 开启通道(只是浏览器和服务器建立连接,现在还不会发送请求)
//
// open(method,url,async,user,psw)
// method:请求的方式,可以是GET,也可以是POST,也可以是其它请求方式
// url:请求的路径
// async:只能是true或者false,true表示此ajax请求是一个异步请求,
// false表示此ajax请求是一个同步请求,大部分请求都是true
// 异步,极少数情况需要同步
// user:用户名
// pwd:密码用,户名和密码是进行身份认证的,说明要想访问这个服务器上
// 的资源,可能需要提供一些口令才能访问,需不需要用户名和密码,
// 主要看服务器的态度
xhr.open("GET", "/ajax/request01", true)
// 4. 发送请求
xhr.send()
}
}
</script>
<input type="button" value="发送ajax" id="btn">
<div id="myDiv"></div>
</body>
</html>
ajax请求的get怎么提交数据?
在"请求行"上提交,格式是:url?name=value&name=value&name=value,也就是在url后面跟上键值对即可
xhr.open("GET", "/ajax/request01?name=zhangsan&age=18", true)
对于低版本的IE浏览器来说,ajax的get请求可能会走缓存,存在缓存问题,对于现代的浏览器来说,大部分浏览器都已经不存在ajax的get缓存问题了
什么是AJAX GET请求缓存问题呢?
- 在HTTP协议中是这样规定get请求的:get请求会被缓存起来,POST请求不会被浏览器缓存
- GET请求缓存的优缺点:
- 优点:直接从浏览器缓存中获取资源,不需要从服务器上重新加载资源,速度较快,用户体验好
- 缺点:无法实时获取最新的服务器资源
- 浏览器什么时候会走缓存?
- 第一:是一个GET请求
- 第二:请求路径已经被浏览器缓存过了,第二次发送请求的时候,这个路径没有变化,会走浏览器缓存
怎么解决GET请求缓存问题呢
给请求路径后面加一个时间戳或者随机数,浏览器一看这个请求没发过,就会主动发送新的get请求了,不会走缓存
下面写一个简单的ajaxPOST请求
html
注意,设置xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")才会模拟成表单提交,不加这句话会报错
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax post</title>
</head>
<body>
<script>
window.onload = function () {
document.getElementById("btn").onclick = function () {
//发送ajax post
// 1 创建xhr对象
var xhr = new XMLHttpRequest();
// 2 创建回调函数
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
document.getElementById("p1").innerHTML = this.responseText
} else {
alert(this.status)
}
}
}
// 3 开启通道
xhr.open("POST", "/ajax/request03", true)
// 4 发送请求
// 模拟form表单,设置请求头的内容类型,非常关键!
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")// 设置请求头的内容类型,模拟form表单提交数据
// 获取网页上的数据
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
// 放到send函数里面的数据,会在请求体中提交数据
// 使用js代码获取用户名和密码,填写进去,注意格式不能乱来,还是键值对&的形式
xhr.send("username=" + username + "&password=" + password)
}
}
</script>
<button id="btn">发送ajax post</button>
user:<input type="text" id="username"><br/>
pwd:<input type="password" id="password"><br/>
<p id="p1"></p>
</body>
</html>
注意,post传参需要将参数放到send函数里,格式是name=value&name=value&name=value的格式,可以拼接字符串
servlet
package com.study.ajax;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/request03")
public class AjaxServlet3 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
// 获取前端的数据
String username = req.getParameter("username");
String password = req.getParameter("password");
PrintWriter writer = resp.getWriter();
writer.print("username:" + username + ",password:" + password);
}
}
下面实现一个文本框填写用户名后,鼠标失焦显示用户名是否可用的功能
- 在前端用户输入用户名之后,失去焦点事件blur发生,然后发送AJAX POST请求,提交用户名
- 在后端,接收到用户名,连接数据库,根据用户名去表中搜索
- 如果用户名已存在
- 后端响应消息:对不起,用户名已存在(在前端页面以红色字体展示)
- 如果用户名不存在
- 后端响应消息:用户名可以使用(在前端页面以绿色字体展示)
- 如果用户名已存在
java
package com.study.ajax;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
// 验证用户名是否可用
@WebServlet("/request04")
public class AjaxServlet4 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
// 连接数据库,验证用户名是否存在
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
boolean flag = false;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=UTC", "root", "root");
String sql = "select * from stu where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
// 用户名存在
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if (flag) {
out.print("<span style=\"color: red\">exist</span>");
} else {
out.print("<span style=\"color: green\">ok</span>");
}
}
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>验证用户名是否可用</title>
</head>
<body>
<script type="text/javascript">
window.onload = function () {
document.getElementById("username").onblur = function () {
//1
var xhr = new XMLHttpRequest();
//2
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200) {
console.log("成功")
document.getElementById("result").innerHTML = this.responseText
} else {
alert("fail, " + this.status)
}
}
}
//3
xhr.open("POST", "/ajax/request04", true)
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
//4
var username = document.getElementById("username").value
xhr.send("username=" + username)
}
}
</script>
用户名:<input type="text" id="username">
<span id="result"></span>
</body>
</html>
JSON格式
后端能不能只返回数据,即不用再返回out.print("<span style=\"color: red\">exist</span>");这样的代码了,而是只给"exit"值让前端自己去渲染,那么Java和JavaScript之间的桥梁就是JSON格式
再JavaScript中如何创建json对象?
var jsonobj = {
"id" : 1,
"name" : "张三",
"age" : true
}
// 属性值的类型随意
如何访问json对象的属性?
console.log(jsonobj.id)console.log(jsonobj["sex"])
但是,后端一般返回的是一个JSON格式的字符串,并不是一个JSON对象,那么怎样将接收到的字符串转换成JSON对象?
-
eval函数
-
调用JavaScript的内置对象JSON的一个方法parse
JSON.parse()
使用JSON字符串响应到浏览器,浏览器解析后渲染
java
package com.study.ajax;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
@WebServlet("/request06")
public class AjaxServlet6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
StringBuilder json = new StringBuilder();
String jsonStr = "";
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=UTC", "root", "root");
String sql = "select name,age,addr from t_user";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
json.append("[");
while (resultSet.next()) {
String name = resultSet.getString("name");
String age = resultSet.getString("age");
String addr = resultSet.getString("addr");
json.append("{\"name\":\"");
json.append(name);
json.append("\",\"age\":\"");
json.append(age);
json.append("\",\"addr\":\"");
json.append(addr);
json.append("\"},");
}
jsonStr = json.substring(0, json.length() - 1) + "]";
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.print(jsonStr);
System.out.println(jsonStr);
}
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript">
window.onload = function () {
document.getElementById("btn").onclick = function () {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200) {
console.log("成功")
var userList = JSON.parse(this.responseText);
var html = ""
for (let i = 0; i < userList.length; i++) {
var user = userList[i]
html += "<tr>"
html += "<td>" + (i + 1) + "</td>"
html += "<td>" + user.name + "</td>"
html += "<td>" + user.age + "</td>"
html += "<td>" + user.addr + "</td>"
html += "</tr>"
}
document.getElementById("result").innerHTML = html
} else {
alert("fail, " + this.status)
}
}
}
//3
xhr.open("GET", "/ajax/request06", true)
//4
xhr.send()
}
}
</script>
<button id="btn">点击获取列表</button>
<table>
<tr>
<td>NO.</td>
<td>name</td>
<td>age</td>
<td>addr</td>
</tr>
<tbody id="result"></tbody>
</table>
</body>
</html>
这里手动拼json字符串太累,可以使用第三方库调用方法,传入一个对象或集合,自动生成json字符串
XML格式
前后端数据交换也可以使用xml,但是没有JSON格式用的多
后端:
-
设置响应的格式
response.setContentType("text/xml;charset=UTF-8"); -
给字符串中拼接标签内容
resp.setContentType("text/xml;charset=UTF-8"); PrintWriter out = resp.getWriter(); StringBuilder xml = new StringBuilder(); xml.append("<students>"); xml.append("<student>"); xml.append("<name>tom</name>"); xml.append("</student>"); xml.append("<student>"); xml.append("<name>jerry</name>"); xml.append("</student>"); xml.append("</students>"); out.print(xml);
前端
-
使用XMLHttpRequest对象的responseXML属性,接收返回之后,可以自动封装成document对象(文档对象)
var xmlDoc = this.responseXML//文档对象 var students = xmlDoc.getElementsByTagName("student");//返回数组 for (let i = 0; i < students.length; i++) { var student = students[i] var properties = student.childNodes console.log(properties[0].textContent)//得到名字name }
对于Tomcat10来说,不会出现乱码问题
Tomcat9及之前的版本Get的响应必须加response.setContentType("text/html;charset=UTF-8");,不加的话响应到浏览器会乱码
Post的请求必须加requset.setCharacterEncoding("UTF-8");,不加的话获取前端请求会乱码
Ajax什么时候选择异步请求,什么时候选择异步请求?
大部分情况都是异步,只有再一些如注册页面,上面的校验用户名是否可用,下面提交注册,这时候校验用户名是否可用的请求只能用同步,否则点击提交上面的校验结果还没回来,可能会将非法数据提交上去
后面的封装没看,地址: www.bilibili.com/video/BV1cR…
看到p22