目录
AJAX(Asynchronous Javascript And Xml)
传统请求及缺点
AJAX概述
绑定事件
XMLHttpRequest对象
AJAX GET请求
AJAX GET请求的缓存问题
AJAX POST请求
JSON对象
基于JSON的数据交互
基于XML的数据交换
AJAX乱码问题
AJAX的异步与同步
AJAX代码封装
AJAX实现省市联动
AJAX跨域问题
跨域
同源策略
AJAX跨域解决方案
AJAX实现搜索联想 自动补全
附录:HTTP状态信息
1xx: 信息
2xx: 成功
3xx: 重定向
4xx: 客户端错误
5xx: 服务器错误
源码放评论区了,需要的自提。
AJAX(Asynchronous Javascript And Xml)
传统请求及缺点
-
传统请求有哪些?
-
直接在浏览器地址栏输入URL。
-
点击超链接
-
提交form表单
-
使用JavaScript代码发送请求
-
window.open(url)
-
document.location.href = url
-
window.location.href = url
-
....
-
-
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>演示传统请求,以及传统请求的缺点</title>
</head>
<body>
<!--直接在浏览器地址栏输入url-->
<!--超链接-->
<a href="/old/request">传统请求</a>
<!--form表单提交-->
<form action="/old/request" method="get"><input type="submit" value="传统请求(form表单提交)">
</form>
<!--通过JS代码来发送请求-->
<input type="button" value="传统请求(通过JS代码发送请求)" onclick="send()">
<script>function send(){//发送请求//window.location.href="/old/request"document.location.href="/old/request"
}
</script>
</body>
</html>
-
传统请求存在的问题
-
页面全部刷新导致了用户的体验差
-
传统的请求导致用户的体验有空白期。
-
AJAX概述
-
AJAX不能称为一种技术,它是多种技术的综合产物
-
AJAX可以让浏览器发送一种特殊的请求,这种请求时异步的
-
什么是同步,什么是异步?
-
假设有t1和t2线程,t1和t2线程并发,就是异步
-
假设有t1和t2线程,t2在执行的时候,必须等待t1线程执行到某个位置之后才能执行。t2在等t1,显然他们是排队的,排队的就是同步
-
AJAX是可以发送异步请求的,也就是说,在同一个浏览器页面中,可以发送多个AJAX请求,这些请求之间不需要等待,是并发的
-
-
AJAX代码属于web前端的JS代码,和后端的JAVA没有关系,后端也可以是php语言,也可以是C语言
-
AJAX应用程序可以使用XML来传输数据,也可以使用纯文本或JSON来传输数据
-
AJAX可以做到在同一个网页中同时启动多个请求,类似于在同一个网页中启动“多线程”,一个“线程”一个“请求”
绑定事件
学习AJAX的前提。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title></title>
</head>
<body><input type="button" value="hello" id="btn">
<script>//页面加载完毕之后,给id="btn"的元素绑定单击事件//这个function就是一个回调函数,当load事件发生之后,这个回调函数才执行。//页面加载完毕之后,load事件发生window.onload = function () {//获取id="btn"的对象var btn = document.getElementById("btn");//给id="btn"绑定click事件//这个function也是回调函数,当按钮被点击的时候,这个回调函数执行btn.onclick = function () {//this代表按钮对象alert(this.value)}}</script>
</body>
</html>
XMLHttpRequest对象
-
XMLHttpRequest对象是AJAX的核心对象,它用来发送请求以及接收服务器数据的返回。
-
XMLHttpRequest对象,现代浏览器都是支持的,都内置了该对象,直接用即可。
-
创建XMLHttpRequest对象:
var xhr=new XMLHttpRequest();
-
XMLHttpRequest对象的方法
方法 描述 open(method,url,async,user,pwd) 规定请求method:请求类型GET或POST 。url:文件位置。 async:true(异步),false(同步)。 user:可选的用户名。 pwd:可选的密码。 send() 将请求发送到服务器,用于GET请求 abort() 取消当前请求 getAllResponseHeaders() 返回头部信息 getResponseHeader() 返回特定的头部信息 send(string) 将请求发送到服务器,用于POST请求 setRequestHeader() 向要发送的报头添加标签/值对 -
XMLHttpRequest对象的属性
属性 | 描述 |
---|---|
readyState | 保存XMLHttpRequest的状态。0:请求未初始化 1:服务器连接已建立 2:请求已收到 3:正在处理请求 4:请求已完成且响应已就绪 |
onreadystatechange | 定义当readyState属性发生改变的时候调用函数 |
responseText | 以字符串返回响应数据 |
responseXML | 以XML数据返回响应数据 |
status | 返回请求的状态码(200、404、500) |
statusText | 返回状态文本(OK、Not Found) |
AJAX GET请求
-
发送AJAX get请求,前端代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>ajax get请求</title> </head> <body> <script>window.onload = function () {document.getElementById("btn").onclick = function () {//发送ajax get请求//1.创建AJAX核心对象XMLHttpRequestvar xhr = new XMLHttpRequest();//2.注册回调函数xhr.onreadystatechange = function () {//这里的回调函数会被多次调用//当XMLHttpRequest对象的readyState的状态是4的时候,表示响应结束了if (this.readyState == 4) {//响应结束之后,一般会有一个Http的状态码//HTTP状态码常见的有:200表示成功,404表示资源找不到,500表示服务器发生错误//HTTP状态码是HTTP协议的一部分,HTTP协议中规定的。服务器相应之后都会有一个状态码。//获取HTTP状态码if (this.status == 404) {alert("对不起,您访问的资源不存在,请检查路径")} else if (this.status == 500) {alert("对不起,服务器内部发生了严重的错误")} else if (this.status == 200) {//通过XMLHttpRequest对象的responseText属性来获取响应的信息//不论服务器响应的是什么,都以普通文本的形式获取。(服务器可能响应回来:普通文本、XML、JSON、HTML...)//innerHTML是JS中的语法,和ajax的XMLHttpRequest对象无关。//innerHTML可以设置元素内部的HTML代码。(innerHTML可以将后面的内容当作一段HTML代码执行)document.getElementById("mydiv").innerHTML = this.responseText;//innerText也不是AJAX的,和XMLHttpRequest无关。//innerText也是设置元素中的内容,但是即使后面是一段HTML代码,也是将其看作一个普通字符串设置进去document.getElementById("mydiv").innerText = this.responseText;}}}//3.开启通道(open只是浏览器和服务器建立连接,通道打开,并不会发送请求)//open(method,url,async,user,pwd)//method:请求的方式,可以是get,也可以是post,也可以是其它请求方式//url:请求的路径//async:只能是true或false,true表示此ajax请求是一个异步请求,false表示此ajax请求是一个同步请求//大部分请求都是true,要求异步//user:用户名 pwd:密码//用户名和密码是进行身份验证的,需不需要用户名和密码,看服务器的要求xhr.open("GET", "/myajax/ajaxrequest1", true);//4.发送请求xhr.send();}} </script> <!--给一个按钮,用户点击这个按钮的时候发送ajax请求--> <input type="button" value="hello" id="btn"> <!--给一个div图层,ajax接收了响应的数据之后,在div中进行渲染--> <div id="mydiv"></div> </body> </html>
-
发送AJAX get请求的后端代码:
package servlet;
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("/ajaxrequest1")
public class AjaxRequest1Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {PrintWriter out = response.getWriter();
//out对象向浏览器输出信息//out在响应的时候,浏览器客户端的XMLHttpRequest对象会接收到这个响应的信息out.print("<font color='red'>welcome!!!</font>");}
}
-
发送AJAX get请求的前端代码的另一种写法
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script>//1.创建AJAX核心对象var xhr = new XMLHttpRequest();
window.onload = function () {document.getElementById("btn").onclick = function () {//2.注册回调函数xhr.onreadystatechange = callback;//3.开启通道xhr.open("GET", "/myajax/ajaxrequest1", true);//4.发送请求xhr.send();}}
function callback() {if (xhr.readyState == 4) {if (xhr.status = 200) {document.getElementById("mydiv").innerHTML = xhr.responseText} else {alert(xhr.status)}}}
</script>
<button id="btn">发送</button>
<div id="mydiv"></div>
</body>
</html>
-
AJAX get请求如何提交数据呢?
-
get请求提交数据是在”请求行“上提交,格式是:
url?name=value&name=value&name=value
-
其实这个get请求提交数据的格式是HTTP协议中规定的,遵循协议即可。
-
AJAX GET请求的缓存问题
-
对于低版本的IE浏览器来说,AJAX的get请求可能会走缓存,存在缓存问题。对于现代的浏览器来说,大部分浏览器已经不存在AJAX的get缓存问题了。
-
什么是AJAX get请求缓存问题?
-
在HTTP协议中,规定get请求会被缓存起来。
-
发送AJAX get请求时,在同一个浏览器上,前后发送的AJAX请求路径一样的话,对于低版本的IE来说,第二次的AJAX get请求会走缓存,不走服务器。
-
-
POST请求在HTTP协议中的规定:POST请求不会被浏览器缓存。
-
GET请求的优缺点:
-
优点:直接从浏览器缓存中获取资源,不需要从服务器上重新加载资源,速度较快,用户体验好。
-
缺点:无法实时获取最新的服务器资源。
-
-
浏览器什么时候会走缓存?
-
GET请求
-
请求路径已经被浏览器缓存过,第二次发送请求的时候,这个路径没有变化,会走缓存。
-
-
如果是低版本的IE浏览器,怎么解决AJAX get请求的缓存问题?
-
可以在请求路径url后面添加时间戳,这个时间戳是随时变化的。所以每一次发送请求的路径都是不一样的,就不会走浏览器的缓存。
-
时间戳:
"url?t="+new Date().getTime()
-
随机数:
"url?t="+Math.random()
-
随机数+事件戳
-
AJAX POST请求
-
AJAX POST请求和GET请求的区别。
//4.发送请求//设置请求头的内容类型(模拟form表单提交的关键代码),必须在open之后xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")//放到send()这个函数的小括号当中的数据,会自动在请求体当中提交数据var username = document.getElementById("username").value;var password = document.getElementById("password").value;//send中的数据就是在请求体当中提交的数据,需要遵循HTTP协议:name=value&name=value...xhr.send("username=" + username + "&password=" + password);
-
实现一个案例:使用AJAX POST请求实现用户注册时用户名是否可用。
-
前端:输入用户名的时候,失去焦点事件blur发生,然后发送AJAX POST请求,提交用户名
-
后端:接收用户名,连接数据库,根据用户名去表中搜索
-
如果用户名已存在,后端响应消息:用户名已存在
-
如果用户名不存在,后端响应消息:用户名可以使用
-
前端代码:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>AJAX POST请求验证用户名是否可用</title> </head> <body> <script type="text/javascript">window.onload = function () { document.getElementById("username").onfocus = function () {document.getElementById("tipMsg").innerHTML = ""} document.getElementById("username").onblur = function () {var xhr = new XMLHttpRequest()xhr.onreadystatechange = function () {if (this.readyState == 4) {if (this.status == 200) {document.getElementById("tipMsg").innerHTML = this.responseText} else {alert(this.status)}}}xhr.open("POST", "/myajax/ur", true)xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")// 获取表单数据var username = document.getElementById("username").valuexhr.send("username=" + username)}} </script> 用户名:<input type="text" id="username"> <span id="tipMsg"></span> </body> </html>
-
后端代码:
package servlet; 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("/ur") public class userRegisterServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 获取用户名String username = request.getParameter("username");// 打布尔标记(一种编程模型)boolean flag = false; // 默认是用户名不存在。// 连接数据库验证用户名是否存在Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ajax?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC", "root", "030522");// 3.获取预编译的数据库操作对象String sql = "select id,name from t_user where name = ?";ps = conn.prepareStatement(sql);ps.setString(1, username);// 4.执行SQL语句rs = ps.executeQuery();// 5.处理结果集if (rs.next()) {// 用户名已存在。flag = true;} } catch (Exception e) {e.printStackTrace();} finally {// 6.释放资源if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (ps != null) {try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}} // 响应结果到浏览器response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();if (flag) {// 用户名已存在,不可用out.print("<font color='red'>对不起,用户名已存在</font>");}else{// 用户名不存在,可以使用out.print("<font color='green'>用户名可以使用</font>");}} }
-
JSON对象
在JavaScript语言中怎样创建一个JSON对象?语法是什么?
var jsonobj = {
"属性名1" : 属性值,
"属性名2" : 属性值,
"属性名3" : 属性值,
"属性名4" : 属性值,
}
注意:属性值可以是数字、布尔类型、字符串、数组、json对象......
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script type="text/javascript">var user = {"usercode": 111,"username": "zhangsan","sex": true,"age": 20,"aihao": ["1", "2", "3"],"addr": {"city": "郑州","street": "二七区","zipcode": 123456}}//访问JSON对象的属性//第一中方式console.log(user.usercode);console.log(user.username);console.log(user.sex ? "男" : "女");console.log(user.age)for (var i = 0; i < user.aihao.length; i++) {console.log(user.aihao[i]);}console.log(user.addr.street);//第二种方式console.log(user["usercode"]);console.log(user["username"]);console.log(user["sex"] ? "男" : "女");console.log(user["age"]);
</script>
</body>
</html>
基于JSON的数据交互
-
从后端java程序种响应回来的是json格式的字符串,如何将json格式的字符串转换成json对象?
//从服务器端返回来的不是一个json对象,是一个json格式的字符串var fromJavaServerJsonStr = "{\"usercode\":111,\"age\":20}";//将json格式的字符串转换为json对象//第一种方式:使用eval函数//第二种方式:调用js语言种的内置对象JSON的一个方法parsevar jsonobj = JSON.parse(fromJavaServerJsonStr);alert(jsonobj.usercode + "," + jsonobj.age);
-
动态展示学员列表前端代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <script>window.onload = function () {document.getElementById("btn").onclick = function () {//1.创建核心对象var xhr = new XMLHttpRequest();//2.注册回调函数xhr.onreadystatechange = function () {if (xhr.readyState == 4) {if (xhr.status == 200) {//document.getElementById("stubody").innerHTML=this.responseText;//将json格式的字符串转换为json对象var stuList = JSON.parse(this.responseText);var html = "";for (var i = 0; i < stuList.length; i++) {var stu = stuList[i];html += "<tr>"html += "<td>" + (i + 1) + "</td>"html += "<td>" + stu.name + "</td>"html += "</tr>"}document.getElementById("stubody").innerHTML = html;} else {alert(this.status);}}}//3.开启通道xhr.open("GET", "/myajax/showList?t=" + new Date().getTime(), true);//4.发送请求xhr.send();}} </script> <input id="btn" type="button" value="显示学生列表"><br> <table width="30%" border="1px"><thead><tr><th>序号</th><th>姓名</th></tr></thead><tbody id="stubody"></tbody> </table> </body> </html>
-
动态展示学员列表后端代码
package servlet;import bean.Student; import com.alibaba.fastjson.JSON; 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.*; import java.util.ArrayList;@WebServlet("/showList") public class showListServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();/*//拼接HTML代码StringBuilder html = new StringBuilder();//目前存在的缺点,后端的java代码中拼接HTML代码,难以维护//如何将数据直接返回给web前端,将页面展示的功能交给前端处理,后端的java代码只返回数据。//返回数据采用JSON的格式,也可以采用XML格式html.append("<tr>");html.append("<td>1</td>");html.append("<td>张三</td>");html.append("</tr>");html.append("<tr>");html.append("<td>2</td>");html.append("<td>李四</td>");html.append("</tr>");out.print(html);*///拼接JSON格式的字符串//String jsonStr="[{\"name\":\"张三\"},{\"name\":\"李四\"}]";//准备StringBuilder对象,拼接JSONStringBuilder json = new StringBuilder();String jsonStr = "";//连接数据库,查询所有的学生,拼接JSON字符串Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;String driver = "com.mysql.cj.jdbc.Driver";String url = "jdbc:mysql://localhost:3306/ajax";String user = "root";String password = "030522";try {Class.forName(driver);conn = DriverManager.getConnection(url, user, password);String sql = "select name from t_student";ps = conn.prepareStatement(sql);rs = ps.executeQuery();/*//开始拼接json.append("[");while (rs.next()) {String name = rs.getString("name");//拼接{"name":" 张三 "},json.append("{\"name\":\"");json.append(name);json.append("\"},");}jsonStr = json.substring(0, json.length() - 1) + "]";*/ArrayList<Student> list = new ArrayList<>();while (rs.next()) {String name = rs.getString("name");//将以上数据封装成Student对象Student student = new Student(name);//将Student对象放到list集合list.add(student);}//将list集合转换成json字符串jsonStr = JSON.toJSONString(list);} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();} finally {try {rs.close();ps.close();conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}//响应JSON格式的字符串给前端out.print(jsonStr);} }
-
注:使用fastjson组件可以将java对象转换成json对象,需要引入jar包。
基于XML的数据交换
-
如果需要服务器端响应XML,响应的内容类型需要写成:
response.setContentType("text/xml;charset=UTF-8");
-
XML和JSON都是常用的数据交换格式
-
XML体积大,解析麻烦,较少用。
-
JSON体积小,解析简单,较常用。
-
-
展示学员列表,前端代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <script>window.onload = function () {document.getElementById("btn").onclick = function () {//1.创建XMLHttpRequest对象var xhr = new XMLHttpRequest();//2.注册回调函数xhr.onreadystatechange = function () {if (xhr.readyState == 4) {if (xhr.status == 200) {//使用XMLHTTPRequest对象的responseXML属性,接收返回值后,可以自动封装成document对象var xmlDoc = this.responseXML//获取所有的<student>元素,返回多个对象,为数组形式var students = xmlDoc.getElementsByTagName("student");var html = "";for (var i = 0; i < students.length; i++) {var student = students[i];//获取<student>元素下的所有子元素html += "<tr>"html += "<td>" + (i + 1) + "</td>"var message = student.childNodes;for (var j = 0; j < message.length; j++) {var node = message[j];if (node.nodeName == "name") {html += "<td>" + node.textContent + "</td>"}if (node.nodeName == "age") {html += "<td>" + node.textContent + "</td>"}}html += "</tr>"}document.getElementById("stutbody").innerHTML = html;} else {alert(this.status)}}}//3.开启通道xhr.open("GET", "/myajax/showList2?t=" + new Date().getTime(), true);//4.发送请求xhr.send();}} </script> <input type="button" id="btn" value="显示学生信息"> <table border="50%" width="1px"><thead><tr><th>序号</th><th>姓名</th><th>年龄</th></tr></thead><tbody id="stutbody"></tbody> </table> </body> </html>
-
展示学员列表,后端代码
package servlet;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("/showList2") public class xmlShowListServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//注意:响应的内容类型是XMLresponse.setContentType("text/xml;charset=utf-8");PrintWriter out = response.getWriter();StringBuilder xml = new StringBuilder();xml.append("<students>");xml.append(" <student>");xml.append(" <name>zhangsan</name>");xml.append(" <age>20</age>");xml.append(" </student>");xml.append(" <student>");xml.append(" <name>lisi</name>");xml.append(" <age>22</age>");xml.append(" </student>");xml.append("</students>");out.print(xml);} }
AJAX乱码问题
-
对于tomcat10,不会出现乱码
-
对于tomcat9
-
响应中文的时候,会出现乱码,如何解决?
response.setContentType("text/html;charset=utf-8");
-
发送post请求时,发送给服务器的数据,服务器接收之后会乱码,如何解决?
request.setCharacterEncoding("utf-8");
-
AJAX的异步与同步
-
什么是异步?什么是同步?
-
ajax请求1和ajax请求2,同时并发,谁也不用等谁,这就是异步。
-
如果ajax请求1在发送的时候需要等待ajax请求2结束之后才能发送,这就是同步。
-
-
代码实现
//假设这个是ajax请求1 //如果第三个参数是false,表示不支持异步。会影响其他ajax请求的发送,只有当我这个请求结束之后,其他的ajax请求才能发送。 xhr1.open("请求方式","URL",false); xhr1.send();//假设这个是ajax请求2 //如果第三个参数是true,表示支持异步请求。ajax请求2发送之后,不影响其他ajax请求的发送。 xhr2.open("请求方式","URL",true); xhr2.send();
-
什么情况下使用同步?(大部分情况下使用ajax异步请求,同步很少用)
-
用户注册
-
用户名需要发送ajax请求进行校验
-
密码需要发送ajax请求进行校验
-
......
-
最终点击注册按钮的时候,也需要发送ajax请求
-
注册的ajax请求和校验的ajax请求不能异步,必须等待所有的校验ajax请求结束之后,注册的ajax请求才能发送。
-
-
AJAX代码封装
-
AJAX请求相关的代码都是类似的,有很多重复的代码,这些重复的代码可以封装成一个工具类。如果发送AJAX请求,就直接调用这个工具类的相关函数即可。
-
手动开发jQuery,源代码
function jQuery(selector) {if (typeof selector == "string") {if (selector.charAt(0) == "#") {domObj = document.getElementById(selector.substring(1));return new jQuery();}}if (typeof selector == "function") {window.onload = selector;}this.html = function (htmlStr) {domObj.innerHTML = htmlStr;}this.click = function (fun) {domObj.onclick = fun;}this.val = function (v) {if (v == undefined) {return domObj.value;} else {domObj.value = v;}}jQuery.ajax = function (jsonArgs) {var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState == 4) {if (xhr.status == 200) {//假设服务器都是json格式var jsonObj = JSON.parse(this.responseText);//调用函数jsonArgs.success(jsonObj);}}}if (jsonArgs.type.toUpperCase() == "POST") {xhr.open("POST", jsonArgs.url, jsonArgs.async);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");xhr.send(jsonArgs.data);}if (jsonArgs.type.toUpperCase() == "GET") {xhr.open("GET", jsonArgs.url + "?" + jsonArgs.data, jsonArgs.async);xhr.send();}} }$ = jQuery; //执行这个的目的是让静态方法ajax生效 new jQuery();
-
前端测试代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <!--引入jQuery库--> <script src="/myajax/js/jQuery-1.0.0.js"></script> <script>$(function () {$("#btn").click(function () {$.ajax({//type: "GET",type:"POST",url: "/myajax/ajaxPackageTestServlet",async: true,data: "username=" + $("#username").val(),success: function (json) {$("#mydiv").html(json.uname)}})})}) </script><input type="button" value="提交" id="btn"><br> 用户名:<input type="text" id="username"><br> <div id="mydiv"></div> </body> </html>
-
后端测试代码
package servlet;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; @WebServlet("/ajaxPackageTestServlet") public class AjaxPackageTestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String username = request.getParameter("username");response.setContentType("text/html;charset=utf-8");// {"uname":"tom"}response.getWriter().print("{\"uname\":\""+username.toUpperCase()+"\"}");}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String username = request.getParameter("username");response.setContentType("text/html;charset=utf-8");// {"uname":"tom"}response.getWriter().print("{\"uname\":\""+username.toUpperCase()+"\"}");} }
AJAX实现省市联动
-
什么是省市联动?
-
在网页上,选择对应的省份之后,动态的关联出该省份对应的市。选择对应的市之后,动态的关联出该市对应的区。
-
-
进行数据库表的设计
t_area (区域表) id(PK-自增) code name pcode ----------------------------------------------- 1 001 河北省 null 2 002 河南省 null 3 003 石家庄 001 4 004 邯郸 001 5 005 郑州 002 6 006 洛阳 002 7 007 涧西区 006将全国所有的省、市、区、县等信息存储在一张表上 采用的存储方式是 code pcode形式
-
建表t_area,模拟好数据。
-
实现功能
-
页面加载完毕之后,将所有的区域信息全部展示
-
选择省份之后,下拉出现该省对应的市
-
选择市之后,下拉出现该市对应的区
-
......
-
-
省市联动前端代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <script src="/myajax/js/jQuery-1.0.0.js"></script> <script>$(function () {$.ajax({type: "GET",url: "/myajax/listAreaServlet",data: "t=" + new Date().getTime(),async: true,success: function (jsonArr) {var html = "<option value=''>--请选择省份--</option>";for (var i = 0; i < jsonArr.length; i++) {var json = jsonArr[i];html += "<option value='" + json.code + "'>" + json.name + "</option>";}$("#sel").html(html);}})//只要change发生,就发送ajax请求$("#sel").change(function () {$.ajax({type: "GET",url: "/myajax/listAreaServlet",data : "t=" + new Date().getTime() + "&pcode=" + this.value,async: true,success: function (jsonArr) {var html = "<option value=''>--请选择市--</option>";for (var i = 0; i < jsonArr.length; i++) {var json = jsonArr[i];html += "<option value='" + json.code + "'>" + json.name + "</option>";}$("#sel2").html(html);}})})}) </script><select id="sel"> </select> <select id="sel2"> </select> </body> </html>
-
省市联动后端代码
package servlet;import bean.Area; import com.alibaba.fastjson.JSON; 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.sql.*; import java.util.ArrayList; import java.util.List;@WebServlet("/listAreaServlet") public class listAreaServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String pcode = request.getParameter("pcode");Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;String driver = "com.mysql.cj.jdbc.Driver";String url = "jdbc:mysql://localhost:3306/ajax?useUnicode=true&characterEncoding=UTF-8";String user = "root";String password = "030522";List<Area> areaList = new ArrayList<>();try {Class.forName(driver);conn = DriverManager.getConnection(url, user, password);String sql = "";if (pcode == null) {sql = "select code,name from t_area where pcode is null";ps = conn.prepareStatement(sql);} else {sql = "select code,name from t_area where pcode=?";ps = conn.prepareStatement(sql);ps.setString(1, pcode);}rs = ps.executeQuery();while (rs.next()) {String code = rs.getString("code");String name = rs.getString("name");Area area = new Area(code, name);areaList.add(area);}} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();} finally {try {rs.close();ps.close();conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}response.setContentType("text/html;charset=utf-8");//使用fastjson将java对象转换成json字符串String jsonStr = JSON.toJSONString(areaList);//响应JSONresponse.getWriter().print(jsonStr);} }
AJAX跨域问题
跨域
-
跨域指的是从一个域名的网页区请求另一个域名的资源。比如从百度页面去请求京东的资源。
-
通过超链接或form表单或window.location.href的方式进行跨域是不存在问题的。但在一个域名的网页中的一段js代码发送ajax请求去访问另一个域名中的资源,由于同源策略的存在导致无法跨域访问,那么ajax就存在这种跨域问题。
同源策略
-
同源策略指的是一段脚本只能读取来自统一来源的窗口和文档的属性,同源就是协议、域名和端口号都相同。
-
同源策略作用:如果你刚刚再网银输入账号密码,查看到了自己的存款信息,紧接着访问一些不安全的网站,这个网站可以访问刚刚的网银站点,并获取账号密码...... 因此,从安全的角度来讲,同源策略是有利于保护网站信息的。
-
区分同源和不同源的三要素:
-
协议
-
域名
-
端口号
-
-
只有协议、域名、端口号均一致,才是同源,其他情况都是不同源
URL1 | URL2 | 是否同源 | 描述 |
---|---|---|---|
http://localhost:8080/a/index.html | http://localhost:8080/a/first | 同源 | 协议 域名 端口一致 |
http://localhost:8080/a/index.html | http://localhost:8080/b/first | 同源 | 协议 域名 端口一致 |
http://www.myweb.com:8080/a.js | https://www.myweb.com:8080/b.js | 不同源 | 协议不同 |
http://www.myweb.com:8080/a.js | http://www.myweb.com:8081/b.js | 不同源 | 端口不同 |
http://www.myweb.com/a.js | MyWeb2.com is for sale | HugeDomains | 不同源 | 域名不同 |
http://www.myweb.com/a.js | http://crm.myweb.com/b.js | 不同源 | 子域名不同 |
-
在某种情况下,需要使用ajax进行跨域访问。比如某公司的A页面有可能需要获取B页面。
AJAX跨域解决方案
方案一:设置响应头
-
核心原理:跨域访问的资源允许你跨域访问。
-
实现:
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // 允许某个
response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有
方案二:jsonp
-
jsonp:json with padding(带填充的json)
-
jsonp不是一个真正的ajax请求,只不过是可以完成ajax的局部刷新效果,解决跨域问题。可以说jsonp是一种类ajax请求的机制。
-
jsonp解决跨域的时候,只支持GET请求,不支持POST请求。
-
应用A前端代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <!--<script src="http://localhost:8081/b/jsonp?fun=sayHello"></script>--> <script>//自定义的函数function sayHello(json) {document.getElementById("mydiv").innerHTML = json.username;}window.onload = function () {document.getElementById("btn").onclick = () => {//创建script元素对象const htmlScriptElement = document.createElement("script");//设置script元素的src属性htmlScriptElement.src = "http://localhost:8081/b/jsonp?fun=sayHello";//将script对象添加到body标签中(加载script)document.getElementsByTagName("body")[0].appendChild(htmlScriptElement);}} </script><input type="button" id="btn" value="jsonp解决跨域问题,达到ajax局部刷新效果"><br> <div id="mydiv"></div> </body> </html>
-
应用B后端代码
package com;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;@WebServlet("/jsonp")
public class jsonpServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//获取函数名String fun = request.getParameter("fun");//响应一段js代码response.getWriter().print(fun + "({\"username\":\"tom\"})");}
}
方案三:jQuery封装的jsonp
-
jQuery库中已经对jsonp进行了封装,可以直接拿来用。
-
用之前需要引入jQuery库的js文件。
-
jQuery中的jsonp其实就是方案二的高度封装,底层原理完全相同。
-
应用A前端代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script src="js/jquery-3.6.0.min.js"></script><script>//自定义函数可以不用写,jQuery可以自动生成//function jQuery3600508253314856699_1655528968612(json){ // 系统自动生成的这个函数默认情况,会自动调用success的回调函数。 }function sayHello(data) {$("#mydiv").html("欢迎" + data.username);}$(function () {$("#btn").click(function () {//发送所谓的ajax请求,本质上不是ajax请求,只是类似ajax请求$.ajax({type: "GET",//jsonp请求只支持get请求//虽然这里的url是这样写的,但实际上发送的请求是:/b/jQueryJsonp?callback=jQuery3600508253314856699_1655528968612&_=1655528968613//callback就是之前写的fun//jQuery3600508253314856699_1655528968612&_=1655528968613就是之前写的sayHello,这个名字是jQuery自动生成的url: "http://localhost:8081/b/jQueryJsonp",dataType: "jsonp",//指定数据类型是jsonp格式。[最关键的一步]/*success: function (data) {$("#mydiv").html("欢迎" + data.username);}*/jsonp: "fun",//指定具体的参数名,不采用默认的回调函数jsonpCallback: "sayHello"//不采用默认的回调函数,用这个属性来指定具体的参数名})})})
</script><button id="btn">jQuery封装的jsonp</button>
<div id="mydiv"></div>
</body>
</html>
-
应用B后端代码
package com;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;@WebServlet("/jQueryJsonp")
public class jQueryJsonpServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//String callback = request.getParameter("callback");//response.getWriter().print(callback + "({\"username\":\"tom\"})");String fun = request.getParameter("fun");response.getWriter().print(fun + "({\"username\":\"tom\"})");}
}
方案四:代理机制(httpclient)
-
使用java程序怎么去发送get/post请求呢?
-
第一种方式:使用JDK内置的API,这些API是可以发送HTTP请求的。
-
第二种方式:使用第三方的开源组件,比如apache的httpclient组件。(httpclient组件时开源免费的,可以直接用)
-
-
在java程序中,使用httpclient组件可以发送http请求。需要先将这个组件相关的jar包引入到项目中。
-
应用A前端代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <script src="js/jQuery-1.0.0.js"></script> <script>$(function () {$("#btn").click(function () {$.ajax({type:"GET",url:"/a/proxy",async:true,success:function (data){$("#mydiv").html(data.username)}})})}) </script><input type="button" value="点击" id="btn"><br> <div id="mydiv"></div> </body> </html>
-
应用A后端代码
package com;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 org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;@WebServlet("/proxy") public class ProxyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 通过httpclient组件,发送HTTP GET请求,访问 TargetServletHttpGet httpGet = new HttpGet("http://localhost:8081/b/target");httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");CloseableHttpClient httpClient = HttpClients.createDefault();HttpResponse resp = httpClient.execute(httpGet);HttpEntity entity = resp.getEntity();BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));String line = null;StringBuffer responseSB = new StringBuffer();while ((line = reader.readLine()) != null) {responseSB.append(line);}reader.close();httpClient.close();// b站点响应回来的数据response.getWriter().print(responseSB);} }
-
应用B后端代码
package com;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;@WebServlet("/target") public class TargetServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.getWriter().print("{\"username\":\"tom\"}");} }
方案五:nginx反向代理
-
nginx反向代理中也是使用了这种代理机制来完成AJAX的跨域,实现起来非常简单,只要修改nginx的配置即可。
AJAX实现搜索联想 自动补全
-
什么是搜索联想,自动补全?
-
百度是一个很典型的代表,在百度的搜索框中输入相关信息的时候,会有搜索联想和自动补全。
-
搜索联想:当用户输入一些单词之后,自动联想出用户要搜索的信息,给一个提示。
-
自动补全:当联想出一些内容之后,用户点击某个联想的单词,然后将这个单词自动补全到搜索框当中。
-
搜索联想和自动补全功能,因为是页面局部刷新效果,所有需要使用ajax请求来实现。
-
-
搜索联想和自动补全的核心实现原理?
-
当键盘事件发生之后,比如keyup:键弹起事件。
-
发送ajax请求,请求中提交用户输入的搜索内容。
-
后端接收ajax请求,执行select语句进行模糊查询(like),返回查询结果。
-
将查询结果封装成json格式的字符串,将字符串响应到前端。
-
前端接收到json格式的字符串之后,解析这个字符串,动态展示页面。
-
-
前端页面
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>ajax实现搜索联想和自动补全功能</title><style>.userInput {width: 300px;height: 25px;font-size: 20px;padding-left: 5px;} .showDataDiv {width: 310px;border: 1px solid lightgray;background-color: antiquewhite;display: none;} .showDataDiv p {padding-left: 5px;margin-top: 2px;margin-bottom: 2px;} .showDataDiv p:hover {cursor: pointer;border: 1px blue solid;background-color: aliceblue;}</style> </head> <body> <script>//使用原生js语言实现搜索联想和自动补全window.onload = () => {document.getElementById("keywords").onkeyup = function () {if (this.value == "") {document.getElementById("datadiv").style.display = "none";} else {const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if (xhr.status >= 200 && xhr.status <= 299) {const json = JSON.parse(xhr.responseText);//遍历数组let html = "";for (let i = 0; i < json.length; i++) {html += "<p onclick='setInput(\"" + json[i].content + "\")'>" + json[i].content + "</p>"}document.getElementById("datadiv").innerHTML = html//显示document.getElementById("datadiv").style.display = "block"}}}xhr.open("GET", "/myajax/query?t=" + new Date().getTime() + "&keywords=" + this.value, true);xhr.send();} }} function setInput(content) {document.getElementById("keywords").value = content;document.getElementById("datadiv").style.display = "none";} </script> <input type="text" class="userInput" id="keywords"> <div id="datadiv" class="showDataDiv"> </div> </body> </html>
-
后端页面
package servlet; 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.sql.*; @WebServlet("/query") public class queryServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//获取用户输入的关键字String keywords = request.getParameter("keywords");String driver = "com.mysql.cj.jdbc.Driver";String url = "jdbc:mysql://localhost:3306/ajax";String user = "root";String password = "030522";Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;StringBuilder builder = new StringBuilder();try {Class.forName(driver);conn = DriverManager.getConnection(url, user, password);String sql = "select content from t_query where content like ?";ps = conn.prepareStatement(sql);ps.setString(1, keywords + "%");rs = ps.executeQuery();builder.append("[");while (rs.next()) {String content = rs.getString("content");builder.append("{\"content\":\"" + content + "\"},");}} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();} finally {try {rs.close();ps.close();conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}response.getWriter().print(builder.subSequence(0, builder.length() - 1) + "]");} }
附录:HTTP状态信息
1xx: 信息
消息: | 描述: |
---|---|
100 Continue | 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。 |
101 Switching Protocols | 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。 |
2xx: 成功
消息: | 描述: |
---|---|
200 OK | 请求成功(其后是对GET和POST请求的应答文档。) |
201 Created | 请求被创建完成,同时新的资源被创建。 |
202 Accepted | 供处理的请求已被接受,但是处理未完成。 |
203 Non-authoritative Information | 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。 |
204 No Content | 没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。 |
205 Reset Content | 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。 |
206 Partial Content | 客户发送了一个带有Range头的GET请求,服务器完成了它。 |
3xx: 重定向
消息: | 描述: |
---|---|
300 Multiple Choices | 多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。 |
301 Moved Permanently | 所请求的页面已经转移至新的url。 |
302 Found | 所请求的页面已经临时转移至新的url。 |
303 See Other | 所请求的页面可在别的url下被找到。 |
304 Not Modified | 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。 |
305 Use Proxy | 客户请求的文档应该通过Location头所指明的代理服务器提取。 |
306 Unused | 此代码被用于前一版本。目前已不再使用,但是代码依然被保留。 |
307 Temporary Redirect | 被请求的页面已经临时移至新的url。 |
4xx: 客户端错误
消息: | 描述: |
---|---|
400 Bad Request | 服务器未能理解请求。 |
401 Unauthorized | 被请求的页面需要用户名和密码。 |
402 Payment Required | 此代码尚无法使用。 |
403 Forbidden | 对被请求页面的访问被禁止。 |
404 Not Found | 服务器无法找到被请求的页面。 |
405 Method Not Allowed | 请求中指定的方法不被允许。 |
406 Not Acceptable | 服务器生成的响应无法被客户端所接受。 |
407 Proxy Authentication Required | 用户必须首先使用代理服务器进行验证,这样请求才会被处理。 |
408 Request Timeout | 请求超出了服务器的等待时间。 |
409 Conflict | 由于冲突,请求无法被完成。 |
410 Gone | 被请求的页面不可用。 |
411 Length Required | "Content-Length" 未被定义。如果无此内容,服务器不会接受请求。 |
412 Precondition Failed | 请求中的前提条件被服务器评估为失败。 |
413 Request Entity Too Large | 由于所请求的实体的太大,服务器不会接受请求。 |
414 Request-url Too Long | 由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况。 |
415 Unsupported Media Type | 由于媒介类型不被支持,服务器不会接受请求。 |
416 | 服务器不能满足客户在请求中指定的Range头。 |
417 Expectation Failed |
5xx: 服务器错误
消息: | 描述: |
---|---|
500 Internal Server Error | 请求未完成。服务器遇到不可预知的情况。 |
501 Not Implemented | 请求未完成。服务器不支持所请求的功能。 |
502 Bad Gateway | 请求未完成。服务器从上游服务器收到一个无效的响应。 |
503 Service Unavailable | 请求未完成。服务器临时过载或当机。 |
504 Gateway Timeout | 网关超时。 |
505 HTTP Version Not Supported | 服务器不支持请求中指明的HTTP协议版本。 |