1. Ajax 异步请求 说明 AJAX, Asynchronous Javascript And XML,异步 JavaScript 和 XML
,一种创建交互式网页应用的网页开发技术。
原理 : 通过在后台与服务器进行少量数据交换,AJAX可以是网页实现异步更新。 这意味着可以在不重新加载整个网页的情况下,对网页的部分内容进行更新
。 (普通不适用AJAX的网页如果需要更新则必须刷新整个页面内容)
作用 :
可以刷新局部页面内容
可以发起异步请求
异步&同步请求 :
同步请求:当页面内容发生改变时,必须全部刷新,且刷新的时候不能发出其他请求。
异步请求:可以局部改变网页的内容,当正在发生改变时,其他的模块内容也可以发出请求。
2. Ajax 实现对象:XMLHttpRequest XMLHttpRequest 异步请求对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。
2.1 xhr 建立 Ajax 流程
new 一个 xhr 对象;
调用 xhr 对象的 open 方法;
send 一些数据;
对服务器的响应过程进行监听,来知道服务器是否正确得做出了响应,接着就可以做一些事情。比如获取服务器响应的内容,在页面上进行呈现。
2.2 xhr 属性、句柄、方法 按照建立 Ajax 流程: ① 创建
new XMLHttpRequest()
创建一个 XMLHttpRequest 对象
② 事件句柄
onreadystatechange
: 用于指定XMLHttpRequest对象状态改变时的事件处理函数
③ 属性
status
/ statusText
:以数字/文本形式返回http状态码
readyState
: XMLHttpRequest对象的处理状态,响应返回成功的时候得到通知 0 :请求未初始化。open() 还没有调用,xhr 对象已创建或已被 abort() 方法重置 1 :服务器连接已建立。open() 已经调用了,但是 send() 方法未调用。请求还没有被发送 2 :请求已经接收。send() 方法已调用,HTTP 请求已发送到 Web 服务器 3 :请求处理中。所有响应头信息已接收,响应体开始接收单未完成 4 :请求已完成。响应已就绪,HTTP 响应已经完全接收,也就是响应完成了
当 status === 200 && readyState === 4
时才去读取响应数据。
responseText
/ responsXML
:获得字符串/XML形式的响应数据
getAllResponseHeader()
:获取所有的响应报头
getResponseHeader()
:查询响应中的某个字段的值
④ 方法
setRequestHeader()
:设置请求头。 模拟form表单,专用于POST请求
1 setRequestHeader ("Content-Type" , "application/x-www-form-urlencoded" );
send()
: 发送数据 GET请求,send 方法不需要携带参数,直接将参数拼接在 open 方法的路径后面 POST 请求,需要 send 方法带参来发送
3. Ajax 异步 GET/POST 请求 服务器 Servlet 处理请求的资源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @WebServlet(name = "TestAjaxServlet", urlPatterns = "/ajax") public class TestAjaxServlet extends HttpServlet { protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ... { String username = request.getParameter("username" ); String password = request.getParameter("password" ); if (username != null && password != null ) { String msg = "账户可用" ; boolean flag = true ; Map<String, Object> map = new HashMap <>(); map.put("flag" , flag); map.put("msg" , msg); ObjectMapper objectMapper = new ObjectMapper (); String resp = objectMapper.writeValueAsString(map); response.setContentType("text/html;charset=utf-8" ); response.getWriter().write(resp); } } protected void doGet (HttpServletRequest request, HttpServletResponse response) throw ... { doPost(request, response); } }
3.1 Ajax GET 请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <title > ajax-get请求</title > <script > function createXMLHttpRequest ( ) { var xmlHttp; try { xmlHttp = new XMLHttpRequest (); } catch (e) { try { xmlHttp = new ActiveXObject ("Msxml2.XMLHTTP" ); } catch (e) { try { xmlHttp = new ActiveXObject ("Microsoft.XMLHTTP" ); } catch (e) { } } } return xmlHttp; } var xhr = createXMLHttpRequest (); xhr.onreadystatechange = function ( ) { console .log ("服务器响应码status = " +xhr.status +", 服务器响应完成状态readyState = " +xhr.readyState ); if (xhr.status === 200 && xhr.readyState === 4 ) { console .log ("请求成功,读取响应完成..." ); var responseText = xhr.responseText ; var jsObj = eval ("(" + responseText + ")" ); console .log (jsObj.flag + ":" + jsObj.msg ); } }; xhr.open ("get" , "${pageContext.request.contextPath}/ajax?username=root&password=1234" , true ); xhr.send (); </script >
3.2 Ajax POST 请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <title > ajax-post请求</title > <script > function createXMLHttpRequest ( ) { var xmlHttp; try { xmlHttp = new XMLHttpRequest (); } catch (e) { try { xmlHttp = new ActiveXObject ("Msxml2.XMLHTTP" ); } catch (e) { try { xmlHttp = new ActiveXObject ("Microsoft.XMLHTTP" ); } catch (e) { } } } return xmlHttp; } var xhr = createXMLHttpRequest (); xhr.onreadystatechange = function ( ) { console .log ("服务器响应码status = " +xhr.status +", 服务器响应完成状态readyState = " +xhr.readyState ); if (xhr.status === 200 && xhr.readyState === 4 ) { console .log ("请求成功,读取响应完成..." ); var responseText = xhr.responseText ; console .log (responseText); var jsObj = eval ("(" + responseText + ")" ); console .log (jsObj.flag ); console .log (jsObj.msg ); } }; xhr.open ("post" , "${pageContext.request.contextPath}/ajax" , true ); xhr.setRequestHeader ("Content-Type" , "application/x-www-form-urlencoded" ); xhr.send ("username=root&password=1234" ); </script >
【注意事项】在以下情况中请使用 POST 请求
:
无法使用缓存文件(更新服务器上的文件或数据库)
向服务器发送大量数据(POST 没有数据量限制)
发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
4. 异步请求无法正确获取返回值 4.1 问题现象 普通的 ajax 校验会出现的问题:无法正确返回 boolean 值 false,因此无法正确拦截表单提交!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $.ajax ({ type : "post" , async : true , url : "<%=request.getContextPath()%>/validateLogin" , data : { "username" : name }, success : function (data ) { console .log ("data:" + data + ", type:" + (typeof data)); if (data == false ) { nameSpan.innerHTML = "√" .fontcolor ("green" ); return true ; } else { nameSpan.innerHTML = "用户名已存在" .fontcolor ("red" ); return false ; } }, dataType : "json" , });
因此,当下的需求非常明确: 如何在 ajax 异步请求回调函数 function(data) 中正确获取一个返回值,用于表单的提交 / 拦截。
进一步研究验证,解决方案有 两种:
方式1:将 ajax 作为临时同步的校验事件,可正确赋值需要的返回值
方式2:使用自定义 get/set 方法确保操作的是永远是同一个 变量
(即需要正确赋值的返回值)
4.2 解决方案 1:改变 async 为临时同步 + 变量赋值 jQuery.ajax() 参数:async
类型:Boolean
默认值: true。默认不带 async 参数时,所有请求均为异步请求。显式设置为 false 则为同步请求。
注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 function checkStuName1 ( ) { var id = document .getElementsByName ("stuId" )[0 ]; var name = document .getElementsByName ("username" )[0 ].value ; var nameSpan = document .getElementById ("nameSpan" ); console .log ("id:" + id + ", " + "name:" + name); if (null === name || "" === name) { nameSpan.innerHTML = "* 用户名不能为空" .fontcolor ("red" ); return false ; } var result; if (undefined === id || null === id || "" === id) { $.ajax ({ type : "post" , async : false , url : "<%=request.getContextPath()%>/validateLogin" , data : { "username" : name }, success : function (data ) { console .log ("data:" + data + ", type:" + (typeof data)); if (data == false ) { nameSpan.innerHTML = "√" .fontcolor ("green" ); result = true ; } else { nameSpan.innerHTML = "用户名已存在" .fontcolor ("red" ); result = false ; } }, dataType : "json" , }); } else { console .log ("修改操作" ); result = false ; } return result; }
4.3 解决方案 2:使用全局变量 + 写 get/set 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 var flag = false ;function getFlag ( ) { return flag; }function setFlag (b ) { flag = b; }function checkStuName2 ( ) { var id = document .getElementsByName ("stuId" )[0 ]; var name = document .getElementsByName ("username" )[0 ].value ; var nameSpan = document .getElementById ("nameSpan" ); console .log ("id:" + id + ", " + "name:" + name); if (null === name || "" === name) { nameSpan.innerHTML = "* 用户名不能为空" .fontcolor ("red" ); return false ; } if (undefined === id || null === id || "" === id) { $.post ("<%=request.getContextPath()%>/validateLogin" , { "username" : name }, function (data ) { console .log ("data:" + data + ", type:" + (typeof data)); if (data == true ) { nameSpan.innerHTML = "用户名已存在" .fontcolor ("red" ); setFlag (false ); } else { nameSpan.innerHTML = "√" .fontcolor ("green" ); setFlag (true ); } }, "json" ); } else { console .log ("修改操作" ); } console .log ("最终flag=" + getFlag ()); return getFlag (); }
4.4 demo 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><!DOCTYPE html > <html > <head > <meta charset ="UTF-8" > <title > 注册/修改页</title > <script src ="${pageContext.request.contextPath}/js/jquery-3.5.1.min.js" > </script > <script type ="text/javascript" > function show (file ) { var reader = new FileReader (); var img = document .getElementById ('img' ); reader.onload = function (evt ) { img.width = "50" ; img.height = "50" ; img.src = evt.target .result ; } reader.readAsDataURL (file.files [0 ]); } function checkStuName1 ( ) { var id = document .getElementsByName ("stuId" )[0 ]; var name = document .getElementsByName ("username" )[0 ].value ; var nameSpan = document .getElementById ("nameSpan" ); console .log ("id:" + id + ", " + "name:" + name); if (null === name || "" === name) { nameSpan.innerHTML = "* 用户名不能为空" .fontcolor ("red" ); return false ; } var result; if (undefined === id || null === id || "" === id) { $.ajax ({ type : "post" , async : false , url : "<%=request.getContextPath()%>/validateLogin" , data : { "username" : name }, success : function (data ) { console .log ("data:" + data + ", type:" + (typeof data)); if (data == false ) { nameSpan.innerHTML = "√" .fontcolor ("green" ); result = true ; } else { nameSpan.innerHTML = "用户名已存在" .fontcolor ("red" ); result = false ; } }, dataType : "json" , }); } else { console .log ("修改操作" ); result = false ; } return result; } var flag = false ; function getFlag ( ) { return flag; } function setFlag (b ) { flag = b; } function checkStuName2 ( ) { var id = document .getElementsByName ("stuId" )[0 ]; var name = document .getElementsByName ("username" )[0 ].value ; var nameSpan = document .getElementById ("nameSpan" ); console .log ("id:" + id + ", " + "name:" + name); if (null === name || "" === name) { nameSpan.innerHTML = "* 用户名不能为空" .fontcolor ("red" ); return false ; } if (undefined === id || null === id || "" === id) { $.post ("<%=request.getContextPath()%>/validateLogin" , { "username" : name }, function (data ) { console .log ("data:" + data + ", type:" + (typeof data)); if (data == true ) { nameSpan.innerHTML = "用户名已存在" .fontcolor ("red" ); setFlag (false ); } else { nameSpan.innerHTML = "√" .fontcolor ("green" ); setFlag (true ); } }, "json" ); } else { console .log ("修改操作" ); } console .log ("最终flag=" + getFlag ()); return getFlag (); } function checkAll ( ) { return checkStuName2 (); } </script > </head > <body > <form action ="<%=request.getContextPath()%>/addOrUpdate" method ="post" enctype ="multipart/form-data" onsubmit ="return checkAll()" > <table > <c:if test ="${null != stuData.stuId}" > <tr > <td > 编号</td > <td > <input type ="text" name ="stuId" readonly ="readonly" value ="${stuData.stuId}" > </td > <td > </td > </tr > </c:if > <tr > <td > 姓名</td > <td > <input type ="text" name ="username" value ="${stuData.stuName}" onblur ="checkStuName2()" > </td > <td > <span id ="nameSpan" > </span > </td > </tr > <tr > <td > 年龄</td > <td > <input type ="text" name ="age" value ="${stuData.stuAge}" > </td > <td > </td > </tr > <tr > <td > 爱好</td > <td > <input type ="text" name ="hobby" value ="${stuData.stuHobby}" > </td > <td > </td > </tr > <tr > <td > 班级</td > <td > <select name ="grade" > <option value ="1" <c:if test ="${stuData.gId==1}" > selected</c:if > >zz2001</option > <option value ="2" <c:if test ="${stuData.gId==2}" > selected</c:if > >zz2002</option > <option value ="3" <c:if test ="${stuData.gId==3}" > selected</c:if > >zz2003</option > <option value ="4" <c:if test ="${stuData.gId==4}" > selected</c:if > >zz2004</option > <option value ="5" <c:if test ="${stuData.gId==5}" > selected</c:if > >zz2005</option > </select > </td > <td > </td > </tr > <tr > <td > 密码</td > <td > <input type ="password" name ="password" value ="${stuData.pwd}" > </td > <td > </td > </tr > <tr > <td > 生日</td > <td > <input type ="date" name ="birthday" value ="${stuData.stuBirthday}" > </td > <td > </td > </tr > <tr > <td > 头像</td > <td > <c:choose > <c:when test ="${not empty stuData.imgPath}" > <img id ="img" height ="50px" width ="50px" alt ="有头像" src ="http://localhost:8081/uploadfile/images/${stuData.imgPath}" > </c:when > <c:otherwise > <img id ="img" height ="50px" width ="50px" alt ="有头像" src ="<%=request.getContextPath()%>/img/default.png" > </c:otherwise > </c:choose > </td > <td > <input type ="file" name ="imgPath" onchange ="show(this)" > </td > </tr > </table > <input type ="submit" value ="注册" > <input type ="reset" value ="重置" > </form > </body > </html >