- A+
JSON&Ajax02
1.Ajax基本介绍
1.1Ajax是什么
- AJAX 即“Asynchronous JavaScript And XML”(异步JavaScript和XML)
- Ajax 是一种浏览器异步发起请求(指定发哪些数据),局部更新页面的技术
- 传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。而使用Ajax,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
- 传统的网页(不使用 AJAX),如果没有得到服务器的响应,浏览器程序会处于一个等待挂起的状态,无法执行其他操作,直至得到http响应。
1.2Ajax经典应用场景
-
搜索引擎根据用户输入关键字,自动提示检索关键字
-
动态加载数据,按需取得数据 [树形菜单,联动菜单]
-
改善用户体验 [输入内容前提示,带进度条文件上传]
-
电子商务应用 [购物车,邮件订阅]
-
访问第三方服务 [访问搜索服务,rss阅读器]
-
页面局部刷新
- Ajax的三个特点
- 异步请求
- 发送指定数据
- 局部刷新
2.Ajax原理示意图
2.1传统的web应用数据通信方式
传统web应用数据通信方式
-
浏览器发出http请求(携带数据username=xxx&pwd=xxx)
-
服务端接收数据,并处理
-
通过http响应,把数据返回给浏览器
缺点:
-
表单提交是把该表单的所有数据都提交给服务端,数据量大,没有意义
-
在服务端没有响应前,浏览器前端页面处于等待状态,处于一个挂起的状态
-
不能进行局部刷新页面,而是刷新整个页面
2.2Ajax数据通信方式
Ajax数据通信方式:
- 浏览器发出http请求,使用XMLHttpRequest对象
- 服务端接收数据,并处理
- 通过http响应,把数据返回给浏览器(返回的数据格式可以多样 如json,xml,普通文本)
优点:
-
可以通过XMLHttpReques对象,发送指定数据,数据量小,速度快
-
XMLHttpReques是异步发送,在服务端没有Http响应前,浏览器不需要等待,用户可以进行其他操作
-
可以进行局部刷新,提高了用户体验
3.JavaScript原生Ajax请求
3.1Ajax文档
AJAX - XMLHttpRequest 对象 (w3school.com.cn)
- onreadystatechange 事件
当请求被发送到服务器时,我们需要执行一些基于响应的任务。
每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState 属性存有 XMLHttpRequest 的状态信息。
下面是 XMLHttpRequest 对象的三个重要的属性:
在 onreadystatechange 事件中,我们规定当服务器响应已做好被处理的准备时所执行的任务。
3.2应用实例
演示JavaScript发送原生Ajax请求的案例
-
在输入框输入用户名
-
点击验证用户名,使用ajax方式,服务端验证该用户名是否已经被占用,如果已经被占用,以json格式返回该用户信息
-
假定用户名为king,就不可用,其他用户名可以(后面我们接入DB)
-
对页面进行局部刷新,显示返回信息
-
思考:为什么直接返回用户名是否可用信息?==>为什么返回json格式的字符串?
可以根据返回的json可以做更多的业务操作。
思路
-
首先创建一个新项目,添加web支持(暂时不使用maven)
-
在web-inf文件夹下添加lib目录,添加servlet和json的相关jar包
-
配置Tomcat服务器
-
创建login.html,使用ajax
大致的流程是:用户点击验证用户名按钮后,操作dom获取填写的用户名,然后创建XMLHttpRequest对象[ajax引擎对象],调用XMLHttpRequest 对象的 open() 和 send() 方法
-
在open中设置三个参数:1.请求方式、2.请求url(如果是get请求,url需包括请求参数)、3.是否使用异步发送
-
给XMLHttpRequest对象绑定一个事件onreadystatechange,该事件的触发时机是XMLHttpRequest的readyState状态改变。
readyState状态详见:AJAX - 服务器响应 (w3school.com.cn)
-
然后调用send方法真正发送ajax请求(如果是post请求,参数就是在send方法中设置)
-
根据readyState的状态判断请求已完成且响应已就绪,然后进行业务操作。
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户注册</title> <script type="text/javascript"> window.onload = function () {//页面加载完毕后执行function var checkButton = document.getElementById("checkButton"); checkButton.onclick = function () { //1.创建XMLHttpRequest对象[ajax引擎对象] var xhr = new XMLHttpRequest(); //2.准备发送指定数据 // 2.1获取用户填写的用户名 var username = document.getElementById("uname").value; // 2.2XMLHttpRequest 对象的 open() 和 send() 方法 // (1)"GET" 请求方式,也可以是post // (2)"/ajax/checkUserServlet?username="+username 就是url // (3) true表示异步发送 xhr.open("GET", "/ajax/checkUserServlet?username=" + username, true); //2.3在send方法调用前,给XMLHttpRequest对象绑定一个事件==>onreadystatechange //每当XMLHttpRequest对象的 readyState 改变时,就会触发 onreadystatechange 事件 xhr.onreadystatechange = function () { //如果请求已完成,并且响应已经就绪,并且状态码是200 if (xhr.readyState == 4 && xhr.status == 200) { //把返回的json数据显示在div上 document.getElementById("div1").innerText = xhr.responseText; // console.log("xhr=", xhr); //处理XMLHttpRequest对象中拿到的数据 var responseText = xhr.responseText; // console.log("返回的信息= "+responseText); if (responseText != "") { //根据在servlet设置的逻辑,如果返回的数据不是空串,说明该用户名不可用 document.getElementById("myres").value = "用户名不可用"; } else { document.getElementById("myres").value = "用户名可用"; } } } //2.4真正发送ajax请求[底层还是http请求] //如果前面open的第一个参数指定的是post请求,那么post的参数在send中指定 //如果open的第一个参数指定的是get请求,那么send中不需要写任何数据,因为前面已经在url中指定了 xhr.send(); } } </script> </head> <body> <h1>用户注册</h1> <form action="/ajax/checkUserServlet" method="post"> 用户名字:<input type="text" name="username" id="uname"> <input type="button" id="checkButton" value="验证用户名"> <input style="border-width: 0;color: red" type="text" id="myres"><br/><br/> 用户密码:<input type="password" name="password"><br/><br/> 电子邮件:<input type="text" name="email"><br/><br/> <input type="submit" value="用户注册"> </form> <h1>返回的 json 数据</h1> <div id="div1"></div> </body> </html>
- 在web.xml中配置servlet
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>CheckUserServlet</servlet-name> <servlet-class>com.li.ajax.servlet.CheckUserServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CheckUserServlet</servlet-name> <url-pattern>/checkUserServlet</url-pattern> </servlet-mapping> </web-app>
- CheckUserServlet:如果接收到的用户名为king,就将其信息以json形式返回前端页面,如果是其他名字,就返回空串。
package com.li.ajax.servlet; import com.google.gson.Gson; import com.li.ajax.entity.User; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; import java.io.PrintWriter; public class CheckUserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //System.out.println("CheckUserServlet 被调用..."); //接收ajax提交的数据 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username");//参数名取决于url中的参数名 System.out.println("uname=" + username); //如果用户名为king,就认为是不可用的 if ("king".equals(username)) {//假定king已经有人使用了 //后面这个信息是从数据库db来获取的 User king = new User(100, "king", "6666", "king@qq.com"); //把king 对象转成 json 格式的字符串 String strKing = new Gson().toJson(king); //返回信息 response.getWriter().print(strKing); } else { //如果用户名可用,返回空串即可 response.getWriter().print(""); } } }
- Javabean,为了完成对象-->json字符串的需求
package com.li.ajax.entity; /** * User类就是一个Javabean/pojo/entity/domain */ public class User { private Integer id; private String name; private String pwd; private String email; public User(Integer id, String name, String pwd, String email) { this.id = id; this.name = name; this.pwd = pwd; this.email = email; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
3.3练习
需求分析:到数据库中验证用户名是否可用
- 点击验证用户名,到数据库中验证用户名是否可用
- 自己设计创建数据库ajaxdb,创建users表
- 使用ajax方式,服务端验证该用户名是否已经被占用了,若已经被占用,以json格式返回该用户信息
- 对页面进行局部刷新,显示返回信息
- 提示:[Mysql+JDBC+数据库连接池]
只需在前面的应用实例中进行升级改进,接入DB
项目的整体架构:
3.3.1创建数据库表格
# 创建数据库 CREATE DATABASE ajaxdb; # 创建users表 CREATE TABLE `user` ( id INT(11) PRIMARY KEY, `username` VARCHAR(32) UNIQUE NOT NULL DEFAULT '', pwd CHAR(32) NOT NULL DEFAULT '', email VARCHAR(32) NOT NULL DEFAULT '' )CHARSET utf8 COLLATE utf8_bin ENGINE INNODB; #插入测试数据 INSERT INTO `user` VALUES(100,"king",MD5('123'),"king@qq.com"); INSERT INTO `user` VALUES(200,"queen",MD5('666'),"queen@qq.com"); INSERT INTO `user` VALUES(300,"princess",MD5('999'),"princess@163.com"); SELECT * FROM `user`; DESC `user`; #drop table `user`; #完成的工作 SELECT COUNT(*) FROM `user` WHERE `username`="king"
3.3.2创建工具类JdbcUtilsByDruid
在项目的lib库下添加德鲁伊,Apache-DBUtils,mysql-connect数据库连接驱动
数据库连接池详见:javase基础-jdbc和数据库连接池
在项目src目录下添加德鲁伊配置文件
#key=value driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/ajaxdb?rewriteBatchedStatements=true username=root password=123456 #initial connection Size initialSize=10 #min idle connecton size minIdle=5 #max active connection size maxActive=20 #max wait time (5000 mil seconds) maxWait=5000
创建德鲁伊工具类JDBCUtilsByDruid
package com.li.ajax.utils; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.sql.*; import java.util.Properties; /** * 基于Druid数据库连接池的工具类 */ public class JDBCUtilsByDruid { private static DataSource ds; //在静态代码块完成ds的初始化 //静态代码块在加载类的时候只会执行一次,因此数据源也只会初始化一次 static { Properties properties = new Properties(); try { /** * 注意,目前我们是javaweb方式启动 * 在web项目下要得到资源文件,需要使用类加载器, * 得到它真正执行的目录下的out/artifacts/项目名/WEB-INF/classes目录中的资源 */ properties.load(JDBCUtilsByDruid.class.getClassLoader() .getResourceAsStream("druid.properties")); //方式二:也可以使用绝对路径 //properties.load(new FileInputStream("D:\IDEA-workspace\ajax\src\druid.properties")); ds = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } //编写getConnection方法 public static Connection getConnection() throws SQLException { return ds.getConnection(); } //关闭连接(注意:在数据库连接池技术中,close不是真的关闭连接,而是将Connection对象放回连接池中) public static void close(ResultSet resultSet, Statement statemenat, Connection connection) { try { if (resultSet != null) { resultSet.close(); } if (statemenat != null) { statemenat.close(); } if (connection != null) { connection.close(); } } catch (SQLException e) { throw new RuntimeException(e); } } }
3.3.3BaseDAO
package com.li.ajax.dao; import com.li.ajax.utils.JDBCUtilsByDruid; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import java.sql.*; /** * 开发BasicDAO,是其他DAO的父类 */ public class BaseDAO<T> {//泛型指定具体的类型 private QueryRunner queryRunner = new QueryRunner(); //查询单行结果 的通用方法(单行多列) public T querySingle(String sql, Class<T> clazz, Object... parameters) { Connection connection = null; try { connection = JDBCUtilsByDruid.getConnection(); return queryRunner.query(connection, sql, new BeanHandler<T>(clazz), parameters); } catch (SQLException e) { throw new RuntimeException(e);//将一个编译异常转变为运行异常 } finally { JDBCUtilsByDruid.close(null, null, connection); } } //根据业务需求还可以有多行多列查询,单行多列插叙,单行单列查询等 //这里不再列举,详见javase基础-满汉楼BasicDAO }
3.3.4User类
修改3.2中的Javabean--User类
package com.li.ajax.entity; /** * User类就是一个Javabean/pojo/entity/domain */ public class User { private Integer id; private String username; private String pwd; private String email; public User() {//注意,一定要有一个空的构造器,方便底层进行反射!! } public User(Integer id, String username, String pwd, String email) { this.id = id; this.username = username; this.pwd = pwd; this.email = email; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + ''' + ", pwd='" + pwd + ''' + ", email='" + email + ''' + '}'; } }
3.3.5UserDAO
package com.li.ajax.dao; import com.li.ajax.entity.User; /** * 对user表的增删查改 */ public class UserDAO extends BaseDAO<User> { //我们的UserDAO继承了BasicDAO,并指定了User //这时我们就可以使用BasicDAO中的方法 }
3.3.6UserService
package com.li.ajax.service; import com.li.ajax.dao.UserDAO; import com.li.ajax.entity.User; import org.testng.annotations.Test; import java.sql.SQLException; /** * UserService 提供业务方法 */ public class UserService { //添加属性UserDAO,方便操作 private UserDAO userDAO = new UserDAO(); //根据用户名返回对应的user对象,如果没有则返回null @Test public User getUserByName(String username) throws SQLException { //使用 User.class 的目的是底层反射创建对象,而反射是调用的无参构造器去创建类, // 因此在对应的实体类中一定要有无参构造器!!! User user = userDAO.querySingle("SELECT * FROM `user` WHERE `username`=?", User.class, username); return user; } }
3.3.7CheckUserServlet
package com.li.ajax.servlet; import com.google.gson.Gson; import com.li.ajax.entity.User; import com.li.ajax.service.UserService; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; import java.sql.SQLException; public class CheckUserServlet extends HttpServlet { //定义一个UserService属性 private UserService userService = new UserService(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //System.out.println("CheckUserServlet 被调用..."); //接收ajax提交的数据 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username");//参数名取决于url中的参数名 System.out.println("uname=" + username); //从数据库中查找有无相同的用户名 try { User userByName = userService.getUserByName(username); if (userByName != null) {//说明用户名存在 // 返回该User对象的json格式的字符串 String strUser = new Gson().toJson(userByName); //给浏览器返回信息 response.getWriter().print(strUser); } else { //如果返回了null,说明没有重名的用户 //用户名可用,返回空串即可 response.getWriter().print(""); } } catch (SQLException e) { e.printStackTrace(); } } }
3.3.8login.html
详见3.2应用实例
3.3.9测试
重启Tomcat,在浏览器中访问http://localhost:8080/ajax/login.html