1.ajax的基本介绍
什么是ajax?
AJAX = Asynchronous JavaScript And XML.
AJAX 并非编程语言。
AJAX 仅仅组合了:
- 浏览器内建的 XMLHttpRequest 对象(从 web 服务器请求数据)
- JavaScript 和 HTML DOM(显示或使用数据)
Ajax 是一个令人误导的名称。Ajax 应用程序可能使用
XML
来传输数据,但将数据作为
纯文本
或
JSON
文本传输也同样常见。
Ajax 允许通过与场景后面的 Web 服务器交换数据来异步更新网页。这意味着可以更新网页的部分,而不需要重新加载整个页面。
2.使用jQuery实现ajax
语法
$.ajax({name:value, name:value, ... })
该参数规定 AJAX 请求的一个或多个名称/值对。
下面的表格中列出了可能的名称/值:
案例
这里是一个案例:
文本框输入值后,当鼠标移出,文本框失去焦点时,就会调用ajax来发送请求,判断文本框内容是否符合要求。
如果用户名的值等于admin时,会出现true,反之则为false。
正确:
错误:
JSP前端页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--导入jQuery,注意点:路径问题--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.js"></script>
</head>
<body>
<%--文本框失去焦点时触发ajax--%>
用户名:<input type="text" id="textname" οnblur="add()"/>
<span id="text1"></span>
<script>
function add() {
$.get({
url: "${pageContext.request.contextPath}/a1",
data: {"name": $("#textname").val()},
success: function (val) {
if (val.toString() === 'true') {
$('#text1').css("color", "green");
} else {
$('#text1').css("color", "red");
}
$("#text1").html(val);
}
})
}
</script>
</body>
</html>
servlet类:
public class AjaxDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
if ("admin".equals(name)) {
resp.getWriter().print("true");
} else {
resp.getWriter().print("false");
}
}
}
当然,别忘了在web.xml中设置相应的路由。
注意点
- 导入jQuery时,要注意路径问题,src的路径要加上${pageContext.request.contextPath}
-
ajax中data数据使用jQuery进行选择html网页中的元素时,要注意选择器,例如id为
textname
的元素需要使用
$("#textname")
- 当路径问题产生时,要重启tomact服务器,不要选择重新部署或者更新类和资源,因为有时候会不起作用(因为这个问题搞了一个下午)
-
在servlet类中需要使用
req.getParameter
来获取前端的传来的参数,返回方法的数值时使用
resp.getWriter().print()
方法 -
如果想使用post方式只需将
$.get
改为
$.post
即可
案例
这里还有一个实例:
点击按钮后,会使用ajax来请求后端数据库中传来的数据,并且渲染在这个表格中。
点击前:
点击后:
JSP前端页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.js"></script>
</head>
<body>
<button οnclick="show()">点击</button>
<table align="center" width="80%">
<tr>
<td>姓名</td>
<td>年龄</td>
<td>专业</td>
</tr>
<tbody id="content"></tbody>
</table>
<script>
function show() {
$.post({
url: "${pageContext.request.contextPath}/a1",
success: function (data) {
console.log(data);
let parse = JSON.parse(data);
html = "";
html += "<tr>" +
"<td>" + parse.name + "</td>" +
"<td>" + parse.age + "</td>" +
"<td>" + parse.major + "</td>" +
"</tr>";
$("#content").html(html);
}
})
}
</script>
</body>
</html>
servlet类:
public class AjaxDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HashMap hashMap = new HashMap();
hashMap.put("name", "zovz");
hashMap.put("age", "18");
hashMap.put("major", "computer");
String str = "{";
Set set = hashMap.keySet();
for (Object o : set) {
str += "\"" + o + "\":" + "\"" + hashMap.get(o) + "\",";
}
resp.getWriter().print(str.substring(0, str.length() - 1) + "}");
}
}
这里创建了hashmap来模拟数据库返回的数据,通过遍历进行字符串的拼接,将它JSON形式的字符串,最后返回到前端时,再转化为JSON对象。
返回的JSON形式的字符串:{“major”:“computer”,“name”:“zovz”,“age”:“18”}
使用jQuery来实现ajax很方便,因为都是已经封装好的,调用只需要记住哪几步就可以实现,但使用一项技术,我们还得了解一下它的原理,接下来我们来看如何使用原生JS来实现ajax。
3. 使用原生Javascript实现ajax
在使用原生JS实现ajax之前,先来介绍一下ajax的基本步骤和涉及的元素的各种方法,以下资料来源于W3school。
这是资料来源地址:https://www.w3school.com.cn/js/js_ajax_intro.asp
ajax如何工作
- 网页中发生一个事件(页面加载、按钮点击)
- 由 JavaScript 创建 XMLHttpRequest 对象
- XMLHttpRequest 对象向 web 服务器发送请求
- 服务器处理该请求
- 服务器将响应发送回网页
- 由 JavaScript 读取响应
- 由 JavaScript 执行正确的动作(比如更新页面)
XMLHttpRequest 对象方法
XMLHttpRequest 对象属性
向服务器发送请求
如需向服务器发送请求,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:
开始案例
使用JS实现ajax一共有以下五个步骤:
第一种情况:发送GET请求:
JSP前端页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<button>点击发送请求</button>
<script>
window.onload = function () {
var btn = document.querySelector("button");
btn.onclick = function () {
//1.创建一个异步对象
var xmlHttp = new XMLHttpRequest();
//2.设置请求地址和请求方式
xmlHttp.open("GET", "${pageContext.request.contextPath}/a2", true);
//3.发送请求
xmlHttp.send();
//4.监听状态的变化
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status >= 200 && xmlHttp.status < 300 || xmlHttp.status === 304) {
//5.返回处理的结果
console.log("接受到服务器返回的结果");
alert(xmlHttp.responseText);
} else {
console.log("没有接受到服务器返回的结果");
}
}
}
}
}
</script>
</body>
</html>
servlet类:
public class test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("HELLO");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
点击后结果:
第二种情况:发送POST请求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>开始</button>
<script>
window.onload = function () {
var oBtn = document.querySelector("button");
oBtn.onclick = function () {
//1.创建一个异步对象
var xmlhttp = new XMLHttpRequest();
//2.设置请求方式和请求地址
//在IE浏览器中需要再每次请求之后更换新的地址
xmlhttp.open("POST", "${pageContext.request.contextPath}/a2", true);
//注意:以下代码必须放到open和send之间
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//3.发送请求
xmlhttp.send("username=zovz&pwd=123456");
//4.监听状态的变化
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4) {
//判断是否请求成功
if (xmlhttp.status >= 200 && xmlhttp.status < 300 || xmlhttp.status === 304) {
//5.处理返回的结果
console.log("接受到服务器返回的结果");
//返回文本
alert(xmlhttp.responseText);
} else {
console.log("没有接受到服务器返回的结果");
}
}
}
}
}
</script>
</body>
</html>
注意点
-
在
IE浏览器
中需要在每次请求之后更换新的地址,即在url后不时地更新参数,以达到不同访问地址的目的,因为IE浏览器会默认将请求过的地址与返回的结果关联起来,如果url重复,会返回第一次返回的结果。 -
xmlhttp.open()中的async参数必须一直都设置为
true
,因为ajax就是实现异步的。 - xmlhttp.status要大于200小于300,但还有304也表示请求成功。
当然,上面这种形式只是为了描述ajax发送请求的五个步骤,将它进行封装后,假设要发送一串数据:
JSP前端页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="myAjax.js"></script>
</head>
<body>
<button>开始</button>
<script>
window.onload = function () {
let btn = document.querySelector("button");
btn.onclick = function () {
ajax("${pageContext.request.contextPath}/a2", {
"username": "zovz",
"pwd": "123456"
}, 3000, function (xhr) {
alert(xhr.responseText);
}, function (xhr) {
alert("请求失败");
})
}
}
</script>
</body>
</html>
myAjax.js:
//将JSP页面的data数据拼接到一起,组成键值对与要发送的url进行合并
function obj2(obj) {
//加上t,表示现在的时间,以确保每次发送的url都不同
obj.t = new Date().getTime();
var res = [];
for (var key in obj) {
res.push(key + "=" + obj[key]);//[username=zovz,pwd=123456]
}
return res.join("&");//username=zovz&pwd=123456
}
function ajax(url, obj, timeout, success, error) {
var str = obj2(obj);
//1.创建一个异步对象
var xmlHttp = new XMLHttpRequest();
//2.设置请求地址和请求方式
xmlHttp.open("GET", url + "?" + str, true);
//3.发送请求
xmlHttp.send();
//4.监听状态的变化
xmlHttp.onreadystatechange = function (ev2) {
if (xmlHttp.readyState == 4) {
if (xmlHttp.status >= 200 && xmlHttp.status < 300 || xmlHttp.status == 304) {
//5.返回处理的结果
success(xmlHttp);
} else {
error(xmlHttp);
}
}
}
//设置超时
if (timeout) {
timer = setInterval(function () {
console.log("中断请求");
xmlHttp.abort();
clearInterval(timer);
}, timeout);
}
}
servlet类:
public class test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
resp.getWriter().print(username + ":" + pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
返回结果:
问题
我们使用原生JS手写的ajax成功运行,并且已经解决了每次发送都会产生新的url,但是还是有以下缺点:
- 如果data值是中文该怎么办,众所周知,中文在url中是不可传递的,url里面只可以存在字母,数字,下划线和ASCll码。
- 如果我想将GET和POST请求写成小写,可以继续传递吗?
- 如果我这次想传递GET请求,但是下一次想传递POST请求,难道要我一直在js代码中修改吗?
- 我想使用ajax,但是传递参数的顺序必须和我之前定义的方法的参数顺序要一致,这会很麻烦,如果我不想遵守这些规则,我想让url在后,而success方法在前,那我该如何做?
首先,我给出问题1的解决方案:
var res = encodeURI("username=张三");
//返回 username=%E5%BC%A0%E4%B8%89
我们可以使用encodeURI方法将键值对中的中文进行编码,再进行传输,然后在服务端接收时,再将它转化为相对应的中文即可。
接下来,我给出问题2和问题3的解决方案:
我们可以在ajax中再多传递一个参数type,可以通过前端指定传输类型来确定使用哪种请求方式。而大小写问题,只需要使用下面的方式即可,因为小写的小写还是小写,我们只需将所传的type都设置为小写。
if (type.toLowerCase() === "GET") {
xmlHttp.open("GET", url + "?" + str, true);
xmlHttp.send();
} else if (type.toLowerCase() === "POST") {
xmlHttp.open("POST", url, true);
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHttp.send(str);
}
下面是我的测试代码:
JSP前端页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="static/js/myAjax.js"></script>
</head>
<body>
<button>发送请求</button>
<script>
window.onload = function () {
let btn = document.querySelector("button");
btn.onclick = function () {
ajax("get","${pageContext.request.contextPath}/a2", {
"username": "张三",
"pwd": "123456"
}, 3000, function (xhr) {
alert(xhr.responseText);
}, function () {
alert("请求失败");
})
}
}
</script>
</body>
</html>
myAjax.js:
function obj2(obj) {
obj.t = new Date().getTime();
var res = [];
for (var key in obj) {
res.push(key + "=" + obj[key]);//[username=zovz,pwd=123456]
}
return res.join("&");//username=zovz&pwd=123456
}
function ajax(type, url, obj, timeout, success, error) {
//使用encodeURI编码
var str =encodeURI(obj2(obj));
//1.创建一个异步对象
var xmlHttp = new XMLHttpRequest();
if (type.toLowerCase() === "get") {
//2.设置请求地址和请求方式
xmlHttp.open("GET", url + "?" + str, true);
//3.发送请求
xmlHttp.send();
} else if (type.toLowerCase() === "post") {
//2.设置请求地址和请求方式
xmlHttp.open("POST", url, true);
//注意:以下代码必须放到open和send之间
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//3.发送请求
xmlHttp.send(str);
}
//4.监听状态的变化
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status >= 200 && xmlHttp.status < 300 || xmlHttp.status === 304) {
//5.返回处理的结果
success(xmlHttp);
} else {
error(xmlHttp);
}
}
}
//设置超时
if (timeout) {
timer = setInterval(function () {
console.log("中断请求");
xmlHttp.abort();
clearInterval(timer);
}, timeout);
}
}
servlet类:
public class test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("TEXT/HTML;charset=utf-8");
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
resp.getWriter().print(username + ":" + pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
返回结果:
接下来给出问题4的解决方案:
因为我不想再遵守方法定义的参数顺序,那我可以传入一个对象,而type,data等作为对象的属性,在传递的过程中使用
对象.属性
的方法就可以解决。
下面是我的测试代码:
JSP前端页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="static/js/myAjax.js"></script>
</head>
<body>
<button>发送请求</button>
<script>
window.onload = function () {
let btn = document.querySelector("button");
btn.onclick = function () {
ajax({
type: "get",
url: "${pageContext.request.contextPath}/a2",
data: {"username": "张三", "pwd": "123456"},
timeout: 3000,
success: function (xhr) {
console.log(xhr.responseText);
alert(xhr.responseText);
},
error: function () {
alert("请求失败");
}
});
}
}
</script>
</body>
</html>
myAjax.js:
function obj2(obj) {
obj.t = new Date().getTime();
var res = [];
for (var key in obj) {
res.push(key + "=" + obj[key]);//[username=zovz,pwd=123456]
}
return res.join("&");//username=zovz&pwd=123456
}
function ajax(object) {
//function ajax(type, url, data, timeout, success, error)
var str = encodeURI(obj2(object.data));
//1.创建一个异步对象
var xmlHttp = new XMLHttpRequest();
if (object.type.toLowerCase() === "get") {
//2.设置请求地址和请求方式
xmlHttp.open("GET", object.url + "?" + str, true);
//3.发送请求
xmlHttp.send();
} else if (object.type.toLowerCase() === "post") {
//2.设置请求地址和请求方式
xmlHttp.open("POST", object.url, true);
//注意:以下代码必须放到open和send之间
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//3.发送请求
xmlHttp.send(str);
}
//4.监听状态的变化
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status >= 200 && xmlHttp.status < 300 || xmlHttp.status === 304) {
//5.返回处理的结果
object.success(xmlHttp);
} else {
object.error(xmlHttp);
}
}
}
//设置超时
if (object.timeout) {
timer = setInterval(function () {
console.log("中断请求");
xmlHttp.abort();
clearInterval(timer);
}, object.timeout);
}
}
这样就实现了我们手写的Ajax方法参数顺序任意,这也就是使用jQuery封装的ajax方法。