AJAX(All InOne)

it2025-05-02  21

AJAX

什么是AJAX

Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)

AJAX是多个技术联合实现的产物

AJAX是一个浏览器客户端上的前端技术

只要是做web开发,B/S架构的,不管服务器端使用什么编程语言,前端AJAX都是要学习的

异步和同步有什么区别

A线程和B线程,并发执行,谁也不等谁,这就是异步

A线程和B线程,在A线程执行的时候,B线程需要等待,或者在B线程执行的时候,A线程需要等待,这就是同步

传统的请求和AJAX请求有什么区别

传统的请求:都是同步的

AJAX请求:可以做到同步

AJAX经典案例

Google auto_complete(输入框自动补全)

Google Map(谷歌地图)

浏览器支持多线程

浏览器本身这个软件是支持多线程并发的,其中ajax请求就是一个线程;

一个页面上可以同时发送多个ajax请求,多个ajax请求对象浏览器多个线程;

当整个浏览器采用的是传统的请求的时候,请求只要一发送,整个浏览器窗口会锁定,无法点击按钮,并且浏览器会将整个窗口当中的数据完全清除,迎接新的页面

AJAX解决的主要问题

解决页面的局部刷新问题

使用AJAX可以在同一个网页中并发的发送多个请求,请求与请求之间互不等待,互不干扰,这样可以提高用户的体验

AJAX 请求

发送ajax请求的代码包括4步

创建ajax核心对象XMLHttpRequest(浏览器内置对象,可以直接使用)注册回调函数开启浏览器和服务器之间的通道发送请求

AJAX对象属性

readyState

XMLHttpRequest对象在请求和响应的过程中,该对象的readyState属性值从 0 到 4 发生变化

0: 请求未初始化 1: 服务器连接已建立 2: 请求已接收 3: 请求处理中 4: 请求已完成,且响应已就绪

status

status属性,HTTP响应状态码

200: “OK” 404: 未找到页面

responseText

responseText属性:响应回来的text

XMLHttpRequest.open()参数

open(method,url,async)

method:请求的类型;GET 或 POSTurl:文件在服务器上的位置async:true(异步)或 false(同步)

async为true时,表示支持异步,同时可以发送多个ajax请求,多个ajax可以同时发送,请求并发 async为false时,表示支持同步,同时仅可发送一个ajax请求,ajax请求执行完后才能发送新的ajax的请求

大部分情况下,都是async为true,支持异步,

但是在一个表单中,有多个项目需要发送ajax请求进行验证的时候,必须等待所有表单项验证完毕后,才允许用户点击注册,此时注册按钮的ajax必须使用同步ajax

AJAX请求(GET)

//1. 创建ajax核心对象XMLHttpRequest(浏览器内置对象,可以直接使用) var xhr; //判断浏览器是否支持XMLHttpRequest对象 if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { //IE 5/6版本不支持XMLHttpRequest,仅支持ActiveXObject对象 xhr = new ActiveXObject("Microsoft.XMLHTTP"); } //2. 注册回调函数 xhr.onreadystatechange = function () { if (xhr.readyState == 4) { //服务器端响应结束 if (xhr.status == 200) { alert("服务器响应成功!"); } else { //弹出错误代码 alert(xhr.status); } } } //3. 开启浏览器和服务器之间的通道 /** * xhr.open(method,url,async); * method:指定请求方式为get/post * url:请求路径 * async:true/false,true表示支持异步,false表示支持同步 * * */ xhr.open("GET","/ajax/checkusername.do?username=",true); //4. 发送请求 xhr.send();

AJAX请求(POST)

POST请求要在第三步xhr.open()后增加以下代码模拟表单POST提交,因为只有表单才能POST提交

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");

POST请求,请求体要在第四步xhr.send()中添加参数,如

xhr.send("test=ajax&method=post"); //1. 创建ajax核心对象XMLHttpRequest var xhr; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } //2. 注册回调函数 xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { alert("AJAX OK"); } } //3. 开启通道 xhr.open("POST","/ajax/userLogin.post",true); //只有表单才能提交POST请求,所以模拟表单,需要加上以下代码 xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8"); //4. 发送请求 //POST请求提交数据在send()方法中提交 xhr.send("test=ajax&method=post");

AJAX请求方式例子

GTE请求

用户注册界面,当用户鼠标失去用户名输入框焦点时,发送ajax请求判断用户名是否被注册 已注册显示红色字体 未注册显示绿色字体

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>使用ajax发送get请求验证用户名是否存在</title> </head> <body> <script type="text/javascript"> function checkUsername(username) { // 1. 创建ajax核心对象XMLHttpRequest(浏览器内置对象,可以直接使用) var xhr; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } // 2. 注册回调函数 xhr.onreadystatechange = function () { if (xhr.readyState == 4) { //服务器端响应结束 if (xhr.status == 200) { var nameTipMsg = document.getElementById("nameTipMsg"); //在浏览器端使用xhr对象接收服务器端响应回来的文本 var text = xhr.responseText; nameTipMsg.innerHTML = text; } else { //弹出错误代码 alert(xhr.status); } } } // 3. 开启浏览器和服务器之间的通道 xhr.open("GET","/ajax/checkusername.do?username="+username,true); // 4. 发送请求 xhr.send(); } </script> 用户名<input type="text" name="username" onblur="checkUsername(this.value)"> <span id="nameTipMsg"></span><br> 密码<input type="password" name="password"> </body> </html>

POST请求

模拟用户登陆 AJAX POST请求,后台返回json字符串, 当{"success":true}时登陆成功,显示绿色登陆成功提示文字; 当{"success":false}时登陆成功,显示红色登陆失败提示文字

<html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>使用ajax发送POST请求验证用户登陆是否成功</title> </head> <body> <script type="text/javascript"> function userLogin() { var username = document.getElementById("username"); var password = document.getElementById("password"); //1. 创建ajax核心对象XMLHttpRequest var xhr ; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } //2. 注册回调函数 xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var jsonString = xhr.responseText;//这里的jsonString只是一个普通的字符串 eval("var jsonObj = " + jsonString);//将字符串作为js代码执行 let tipMsg = document.getElementById("tipMsg"); if (jsonObj.success) { tipMsg.innerHTML = "<font color='green'>登陆成功!</font>"; } else { tipMsg.innerHTML = "<font color='red'>登陆失败!</font>"; } } } //3. 开启通道 xhr.open("POST","/ajax/userLogin.post",true); //只有表单才能提交POST请求,所以模拟表单,需要加上以下代码 xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8"); //4. 发送请求,POST请求提交数据在send()方法中提交 xhr.send("username="+username.value+"&password="+password.value); } </script> 用户名<input type="text" id="username"><span id="tipMsg"></span><br> 密码<input type="password" id="password"><br> <input type="button" value="登陆" onclick="userLogin()"> </body> </html>

GET请求缓存问题

问题:get请求,第一次会请求服务器,之后再访问的时候就直接走浏览器缓存,而不再请求服务器了

解决:可以在get请求的URL加上时间戳,这样每次请求的URL都不一样,这样每次都会请求服务器了

var timeStamp = new Date().getTime();//获取毫秒数 xhr.open("GET","/ajax/checkusername.get?_="+timeStamp+"&username="+username,true);

完整代码

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>使用ajax发送get请求验证用户名是否存在</title> </head> <body> <script type="text/javascript"> function checkUsername(username) { var xhr; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { var nameTipMsg = document.getElementById("nameTipMsg"); var text = xhr.responseText; nameTipMsg.innerHTML = text; } else { alert(xhr.status); } } } // 3. 开启浏览器和服务器之间的通道 // 解决get换出问题,在URL中加上时间戳 var timeStamp = new Date().getTime();//获取毫秒数 xhr.open("GET","/ajax/checkusername.get?_="+timeStamp+"&username="+username,true); // 4. 发送请求 xhr.send(); } </script> 用户名<input type="text" name="username" onblur="checkUsername(this.value)"> <span id="nameTipMsg"></span><br> 密码<input type="password" name="password"> </body> </html>

省市联动案例

打开网页时初始化,浏览器发送请求获取省份列表;

当用户选择省份时,发送请求获取此省份的市列表

使用 自定义src/Utils.JDBCUtils工具类使用 Druid数据库连接池

资源与准备

使用的工具类与jar包

自定义src/Utils.JDBCUtils工具类

自定义src/prop/druid.properties配置文件用来配置druid数据库连接池

自定义数据库SQL/ajax.sql文件

官方的JDBC驱动jar包/WEB-INF/lib/mysql-connector-java-5.1.7-bin.jar

官方的druid数据库连接池jar包/WEB-INF/lib/druid-1.1.23.jar

使用的资源详情

src/Utils.JDBCUtilsJDBC工具类

package Utils; import java.sql.*; import com.alibaba.druid.pool.DruidDataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.ResourceBundle; /** * @version 1.0 * @Description: * <p>JDBCUtils 工具类</p> * <p>是一个工具类,提供了数据库连接池和常用的资源关闭方法</p> * @className JDBCUtils * @author: Mango * @date: 2020-09-06 21:22 */ public class JDBCUtils { /** * Druid连接池对象 */ private static DruidDataSource druidDataSource = new DruidDataSource(); /** * 静态创建一个Druid连接池对象并通过配置文件设置相关配置 */ static { ResourceBundle bundle = ResourceBundle.getBundle("prop/druid"); druidDataSource.setDriverClassName(bundle.getString("driverClassName")); druidDataSource.setUrl(bundle.getString("url")); druidDataSource.setUsername(bundle.getString("username")); druidDataSource.setPassword(bundle.getString("password")); druidDataSource.setInitialSize(Integer.parseInt(bundle.getString("initialSize"))); druidDataSource.setMaxActive(Integer.parseInt(bundle.getString("maxActive"))); druidDataSource.setMaxActive(Integer.parseInt(bundle.getString("maxWait"))); } /** * 获取连接 * <p>从druid连接池中获取一个连接</p> * @param * @return 返回一个Connection连接对象 */ public static Connection getConnection() throws SQLException { return druidDataSource.getConnection(); } /** * 关闭连接和资源 * <p>关闭Connection连接、关闭PreparedStatement预编译SQL语句对象、关闭ResultSet查询结果集</p> * @param conn 数据库连接对象 * @param ps 预编译SQL语句对象 * @param rs 查询结果集 * @return void */ public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } /** * 关闭连接和资源 * <p>关闭Connection连接、关闭PreparedStatement预编译SQL语句对象</p> * @param conn 数据库连接对象 * @param ps 预编译SQL语句对象 * @return void */ public static void closeAll(Connection conn, PreparedStatement ps) { if (ps != null) { try { ps.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } /** * 关闭数据库连接 * <p>关闭数据库连接对象conn</p> * @param conn 数据连接对象 * @return void */ public static void closeConnection(Connection conn) { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } /** * 关闭statement * <p>关闭数据库连接对象conn</p> * @param statement statement对象 * @return void */ public static void closeStatement(Statement statement) { if (statement != null) { try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } /** * 关闭ResultSet查询结果集 * <p>关闭查询结果集对象ResultSet rs</p> * @param rs 查询结果集 * @return void */ public static void closeResultSet(ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } }

src/prop/druid.properties数据库连接池配置文件

url=jdbc:mysql://localhost:3306/messagewall?useUnicode=true&characterEncoding=utf8 username=root password=admin driverClassName=com.mysql.jdbc.Driver initialSize=1 maxActive=3 maxWait=60000

SQL/ajax.sql数据库文件

数据库名为 ajax

/* Navicat Premium Data Transfer Source Server : local Source Server Type : MySQL Source Server Version : 50562 Source Host : localhost:3306 Source Schema : ajax Target Server Type : MySQL Target Server Version : 50562 File Encoding : 65001 Date: 16/10/2020 23:15:10 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_city -- ---------------------------- DROP TABLE IF EXISTS `t_city`; CREATE TABLE `t_city` ( `code` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `pcode` char(3) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`code`) USING BTREE, INDEX `pcode`(`pcode`) USING BTREE, CONSTRAINT `t_city_ibfk_1` FOREIGN KEY (`pcode`) REFERENCES `t_province` (`code`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_city -- ---------------------------- INSERT INTO `t_city` VALUES (1, '保定市', '003'); INSERT INTO `t_city` VALUES (2, '石家庄市', '003'); INSERT INTO `t_city` VALUES (3, '廊坊市', '003'); INSERT INTO `t_city` VALUES (4, '张家口市', '003'); INSERT INTO `t_city` VALUES (5, '济南市', '001'); INSERT INTO `t_city` VALUES (6, '淄博市', '001'); INSERT INTO `t_city` VALUES (7, '青岛市', '001'); INSERT INTO `t_city` VALUES (8, '烟台市', '001'); INSERT INTO `t_city` VALUES (9, '太原市', '002'); INSERT INTO `t_city` VALUES (10, '运城市', '002'); INSERT INTO `t_city` VALUES (11, '大同市', '002'); INSERT INTO `t_city` VALUES (12, '临汾市', '002'); INSERT INTO `t_city` VALUES (13, '郑州市', '004'); INSERT INTO `t_city` VALUES (14, '开封市', '004'); INSERT INTO `t_city` VALUES (15, '洛阳市', '004'); INSERT INTO `t_city` VALUES (16, '平顶山市', '004'); -- ---------------------------- -- Table structure for t_province -- ---------------------------- DROP TABLE IF EXISTS `t_province`; CREATE TABLE `t_province` ( `code` char(3) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`code`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_province -- ---------------------------- INSERT INTO `t_province` VALUES ('001', '山东省'); INSERT INTO `t_province` VALUES ('002', '山西省'); INSERT INTO `t_province` VALUES ('003', '河北省'); INSERT INTO `t_province` VALUES ('004', '河南省'); SET FOREIGN_KEY_CHECKS = 1;

业务代码

/WEB-INF/index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>省市联动</title> </head> <body> <select id="provinceList"> <option value='-1'>请选择省份区</option> </select> <select id="cityList"> <option value=''>请选择市区</option> </select> </body> </html> <script type="text/javascript" src="index.js"></script>

/WEB-INF/index.js

window.onload = function () { //省、市下拉列表对象 let provinceListObj = document.getElementById("provinceList"); let cityListObj = document.getElementById("cityList"); //给省下拉列表注册onchange provinceListObj.onchange = function () { listCity(); } /** * 获取省列表函数 * 向服务器发送请求,获取json字符串,然后将数据设置到 省 下拉列表中 */ function listProvince() { var xhr; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var jsonString = xhr.responseText; eval("var arr = " + jsonString); // 遍历json数组 var html = "<option value='-1'>请选择省份</option>"; for (var i=0; i<arr.length; i++) { var provinceJson = arr[i]; html += "<option value='"+provinceJson.code+"'>"+provinceJson.name+"</option>" } provinceListObj.innerHTML = html; } } xhr.open("GET","/ajax_03/province/list.do",true); xhr.send(); } /** * 获取城市列表函数 * 向服务器发送请求,获取json字符串,然后将数据设置到 市 下拉列表中 * 此函数在省下拉列表改变时调用 */ function listCity() { //判断,如果选择了 省 默认提示选项,则清空 市 下拉列表中的项 if (provinceListObj.value == "-1") { cityListObj.innerHTML = "<option value=''>请选择市区</option>"; return; } var xhr; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var jsonString = xhr.responseText; eval("var arr = " + jsonString); // 遍历json数组 var html = "<option value=''>请选择市区</option>"; for (var i=0; i<arr.length; i++) { var cityJson = arr[i]; html += "<option value='"+cityJson.code+"'>"+cityJson.name+"</option>" } cityListObj.innerHTML = html; } } var pcode = provinceListObj.value; xhr.open("GET","/ajax_03/city/list.do?pcode="+pcode,true); xhr.send(); } //执行此函数获取省列表 listProvince(); }

src/action.CityListAction.java

package action; import utils.JDBCUtils; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.*; /** * <p>获取 市 列表,拼接json,将json返回浏览器</p> * @className ProvinceListAction * @date: 2020-10-16 21:39 */ @WebServlet("/city/list.do") public class CityListAction extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String pcode = request.getParameter("pcode"); //局部变量不存在线程安全问题,所以建议使用StringBuilder,效率高 /* [ {"code":"001","name":"济南市"}, {"code":"002","name":"淄博市"}, {"code":"003","name":"青岛市"}, {"code":"003","name":"烟台市"} ] */ StringBuilder json = new StringBuilder(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement("select * from t_city where pcode=?"); ps.setString(1,pcode); rs = ps.executeQuery(); json.append("["); while (rs.next()) { String code = rs.getString("code"); String name = rs.getString("name"); // {"code":"001","name":"济南市"}, json.append("{"); json.append("\"code\":"); json.append("\""+code+"\","); json.append("\"name\":"); json.append("\""+name+"\""); json.append("},"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JDBCUtils.closeAll(conn,ps,rs); } //截取字符串,去掉最后一个多余的逗号, String endJson = json.substring(0, json.length() - 1) + "]"; response.setContentType("text/html;charset=utf-8"); response.getWriter().print(endJson); } }

src/action.ProvinceListAction.java

package action; import utils.JDBCUtils; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.*; /** * <p>获取 省 列表,拼接json,将json返回浏览器</p> * @className ProvinceListAction * @date: 2020-10-16 21:39 */ @WebServlet("/province/list.do") public class ProvinceListAction extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //局部变量不存在线程安全问题,所以建议使用StringBuilder,效率高 /* [ {"code":"001","name":"山东省"}, {"code":"002","name":"山西省"}, {"code":"003","name":"河南省"} ] */ StringBuilder json = new StringBuilder(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement("select code,name from t_province order by code asc"); rs = ps.executeQuery(); json.append("["); while (rs.next()) { String code = rs.getString("code"); String name = rs.getString("name"); // {"code":"001","name":"山东省"}, json.append("{"); json.append("\"code\":"); json.append("\""+code+"\","); json.append("\"name\":"); json.append("\""+name+"\""); json.append("},"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JDBCUtils.closeAll(conn,ps,rs); } //截取字符串,去掉最后一个多余的逗号, String endJson = json.substring(0, json.length() - 1) + "]"; response.setContentType("text/html;charset=utf-8"); response.getWriter().print(endJson); } }
最新回复(0)