AJAX基础+Axios快速入门+JSON使用+综合案例
1、 AJAX
1.1 概述
AJAX
(Asynchronous JavaScript And XML):异步的 JavaScript 和 XML。
1.1.1 作用
-
与服务器进行数据交换
-
使用JSP
- 浏览器发送请求
Servlet
Servlet
调用完业务逻辑层之后,将数据存储到Request域对象
中- 转发到对应
JSP
进行展示(将JSP
作为视图)
- 浏览器发送请求
-
使用AJAX:AJAX可以给服务器发送请求,并获取服务器响应的数据
使用AJAX+HTML替换JSP
- 浏览器发送请求
Servlet
Servlet
调用完业务逻辑层后将数据通过AJAX
直接响应回给浏览器页面- 页面使用
HTML
来进行数据展示
- 浏览器发送请求
-
-
异步交互
- 可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用校验,点击关注,点击收藏,等等…
1.1.2 同步和异步
-
同步发送请求的过程
- 客户端访问服务器
- 服务器处理客户端请求
- 服务器返回处理结果到客户端
- 客户端进行其他请求
-
异步发送请求的过程:不用等待服务器端
- 客户端访问服务器
- 服务器处理客户端请求(期间客户端可进行其他操作)
- 服务器返回处理结果到客户端
- 客户端进行其他请求
1.2 快速入门
1.2.1 服务端实现
-
编写Servlet
package priv.dandelion.controller.servlet; 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; @WebServlet("/ajaxDemo") public class AjaxDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 响应数据 resp.getWriter().write("hello ajax~"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
1.2.2 客户端实现
-
步骤说明
- 创建
xhttp
核心对象 - 建立连接
- 发送请求
- 获取响应
- 创建
-
完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX-demo1</title> </head> <body> <script> // 创建核心对象 var xhttp; if (window.XMLHttpRequest) { xhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 xhttp = new ActiveXObject("Microsoft.XMLHTTP"); } // 建立连接(包含第三个参数,为true是异步,默认异步,此处省略) xhttp.open("GET", "http://localhost:8080/ajax-demo/ajaxDemo"); // 发送请求 xhttp.send(); // 获取响应,监听到onreadystatechange就绪状态事件,事件发生变化时执行函数 xhttp.onreadystatechange = function() { // this.readyState == 4 表示请求发送结束且响应就绪,this.status == 200表示成功收到响应 if (this.readyState == 4 && this.status == 200) { // 通过 this.responseText 可以获取到服务端响应的数据 alert(this.responseText); } }; </script> </body> </html>
1.3 案例
1.3.1 需求
-
用户注册功能
-
在用户名输入框失去焦点时,校验该用户名是否存在,并在页面中给出提示
1.3.2 分析
-
前端
- 给用户名输入框绑定光标失去焦点事件
onblur
- 发送ajax请求,携带
username
参数
- 给用户名输入框绑定光标失去焦点事件
-
后端
- 接收用户名
- 调用
service
查询User
- 返回标记
1.3.2 后端实现
-
环境准备
-
依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>priv.dandelion</groupId> <artifactId>ajax-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- junit单元测试依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--mybatis 依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!--mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!-- 添加slf4j日志api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</version> </dependency> <!-- 添加logback-classic依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- 添加logback-core依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <!-- servlet依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- JSP依赖 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!-- JSTL依赖 --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- JSTL标准标签库依赖 --> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <!-- tomcat插件 --> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project>
-
SQL
-- 删除tb_user表 drop table if exists tb_user; -- 创建用户表 CREATE TABLE tb_user( id int primary key auto_increment, username varchar(20) unique, password varchar(32) ); -- 添加数据 INSERT INTO tb_user(username,password) values('zhangsan','123'),('lisi','234'); SELECT * FROM tb_user;
-
静态页面
-
CSS
* { margin: 0; padding: 0; list-style-type: none; } .reg-content{ padding: 30px; margin: 3px; } a, img { border: 0; } body { background-image: url("../imgs/reg_bg_min.jpg") ; text-align: center; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; height: 90px; } .inputs{ vertical-align: top; } .clear { clear: both; } .clear:before, .clear:after { content: ""; display: table; } .clear:after { clear: both; } .form-div { background-color: rgba(255, 255, 255, 0.27); border-radius: 10px; border: 1px solid #aaa; width: 424px; margin-top: 150px; margin-left:1050px; padding: 30px 0 20px 0px; font-size: 16px; box-shadow: inset 0px 0px 10px rgba(255, 255, 255, 0.5), 0px 0px 15px rgba(75, 75, 75, 0.3); text-align: left; } .form-div input[type="text"], .form-div input[type="password"], .form-div input[type="email"] { width: 268px; margin: 10px; line-height: 20px; font-size: 16px; } .form-div input[type="checkbox"] { margin: 20px 0 20px 10px; } .form-div input[type="button"], .form-div input[type="submit"] { margin: 10px 20px 0 0; } .form-div table { margin: 0 auto; text-align: right; color: rgba(64, 64, 64, 1.00); } .form-div table img { vertical-align: middle; margin: 0 0 5px 0; } .footer { color: rgba(64, 64, 64, 1.00); font-size: 12px; margin-top: 30px; } .form-div .buttons { float: right; } input[type="text"], input[type="password"], input[type="email"] { border-radius: 8px; box-shadow: inset 0 2px 5px #eee; padding: 10px; border: 1px solid #D4D4D4; color: #333333; margin-top: 5px; } input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus { border: 1px solid #50afeb; outline: none; } input[type="button"], input[type="submit"] { padding: 7px 15px; background-color: #3c6db0; text-align: center; border-radius: 5px; overflow: hidden; min-width: 80px; border: none; color: #FFF; box-shadow: 1px 1px 1px rgba(75, 75, 75, 0.3); } input[type="button"]:hover, input[type="submit"]:hover { background-color: #5a88c8; } input[type="button"]:active, input[type="submit"]:active { background-color: #5a88c8; } .err_msg{ color: red; padding-right: 170px; } #password_err,#tel_err{ padding-right: 195px; } #reg_btn{ margin-right:50px; width: 285px; height: 45px; margin-top:20px; } #checkCode{ width: 100px; } #changeImg{ color: aqua; }
-
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>欢迎注册</title> <link href="css/register.css" rel="stylesheet"> </head> <body> <div class="form-div"> <div class="reg-content"> <h1>欢迎注册</h1> <span>已有帐号?</span> <a href="login.html">登录</a> </div> <form id="reg-form" action="#" method="get"> <table> <tr> <td>用户名</td> <td class="inputs"> <input name="username" type="text" id="username"> <br> <span id="username_err" class="err_msg" style="display: none">用户名不太受欢迎</span> </td> </tr> <tr> <td>密码</td> <td class="inputs"> <input name="password" type="password" id="password"> <br> <span id="password_err" class="err_msg" style="display: none">密码格式有误</span> </td> </tr> <tr> <td>验证码</td> <td class="inputs"> <input name="checkCode" type="text" id="checkCode"> <img src="imgs/a.jpg"> <a href="#" id="changeImg">看不清?</a> </td> </tr> </table> <div class="buttons"> <input value="注 册" type="submit" id="reg_btn"> </div> <br class="clear"> </form> </div> </body> </html>
-
-
数据库
-
核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--起别名--> <typeAliases> <package name="priv.dandelion.entity"/> </typeAliases> <environments default="development"> <environment id="development"> <!-- 采用JDBC的事务管理方式 --> <transactionManager type="JDBC"/> <!-- 数据库连接信息 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- value的值一定不能换行,一定!一定!不能换行 --> <property name="url" value="jdbc:mysql:///db1?useSSL=false&useUnicode=true&characterEncoding=utf-8&useServerPrepStmts=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 扫描mapper,加载SQL映射文件 --> <mappers> <package name="priv.dandelion.dao"/> </mappers> </configuration>
-
Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="priv.dandelion.dao.UserMapper"> </mapper>
-
-
工具类
-
获取SqlSessionFactory
package priv.dandelion.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class SqlSessionFactoryUtils { // 提升作用域,用于再方法内进行返回 private static SqlSessionFactory sqlSessionFactory; // 静态代码块会随着类的加载自动执行且只执行一次 static { String resource = "mybatis-config.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } public static SqlSessionFactory getSqlSessionFactory() { return sqlSessionFactory; } }
-
重编码ISO-8859-1为UTF-8
package priv.dandelion.utils; import java.nio.charset.StandardCharsets; public class ReEncoding { public static String reEncodingToUtf8(String str) { // 使用ISO-8859-1编码将乱码字符编回二进制码,得到其字节数组 byte[] bytes = str.getBytes(StandardCharsets.ISO_8859_1); // 将字节数组使用UTF-8重新编码 return new String(bytes, StandardCharsets.UTF_8); } }
-
验证码生成
package priv.dandelion.utils; import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.Random; /** * 生成验证码工具类 */ public class CheckCodeUtil { public static final String VERIFY_CODES = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static Random random = new Random(); /** * 输出随机验证码图片流,并返回验证码值(一般传入输出流,响应response页面端,Web项目用的较多) * * @param width 图片宽度 * @param height 图片高度 * @param os 输出流 * @param verifySize 验证码长度 * @return String * @throws IOException */ public static String outputVerifyImage(int width, int height, OutputStream os, int verifySize) throws IOException { String verifyCode = generateVerifyCode(verifySize); outputImage(width, height, os, verifyCode); return verifyCode; } /** * 使用系统默认字符源生成验证码 * * @param verifySize 验证码长度 * @return */ public static String generateVerifyCode(int verifySize) { return generateVerifyCode(verifySize, VERIFY_CODES); } /** * 使用指定源生成验证码 * * @param verifySize 验证码长度 * @param sources 验证码字符源 * @return */ public static String generateVerifyCode(int verifySize, String sources) { // 未设定展示源的字码,赋默认值大写字母+数字 if (sources == null || sources.length() == 0) { sources = VERIFY_CODES; } int codesLen = sources.length(); Random rand = new Random(System.currentTimeMillis()); StringBuilder verifyCode = new StringBuilder(verifySize); for (int i = 0; i < verifySize; i++) { verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1))); } return verifyCode.toString(); } /** * 生成随机验证码文件,并返回验证码值 (生成图片形式,用的较少) * * @param w * @param h * @param outputFile * @param verifySize * @return * @throws IOException */ public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException { String verifyCode = generateVerifyCode(verifySize); outputImage(w, h, outputFile, verifyCode); return verifyCode; } /** * 生成指定验证码图像文件 * * @param w * @param h * @param outputFile * @param code * @throws IOException */ public static void outputImage(int w, int h, File outputFile, String code) throws IOException { if (outputFile == null) { return; } File dir = outputFile.getParentFile(); //文件不存在 if (!dir.exists()) { //创建 dir.mkdirs(); } try { outputFile.createNewFile(); FileOutputStream fos = new FileOutputStream(outputFile); outputImage(w, h, fos, code); fos.close(); } catch (IOException e) { throw e; } } /** * 输出指定验证码图片流 * * @param w * @param h * @param os * @param code * @throws IOException */ public static void outputImage(int w, int h, OutputStream os, String code) throws IOException { int verifySize = code.length(); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Random rand = new Random(); Graphics2D g2 = image.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 创建颜色集合,使用java.awt包下的类 Color[] colors = new Color[5]; Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.YELLOW}; float[] fractions = new float[colors.length]; for (int i = 0; i < colors.length; i++) { colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)]; fractions[i] = rand.nextFloat(); } Arrays.sort(fractions); // 设置边框色 g2.setColor(Color.GRAY); g2.fillRect(0, 0, w, h); Color c = getRandColor(200, 250); // 设置背景色 g2.setColor(c); g2.fillRect(0, 2, w, h - 4); // 绘制干扰线 Random random = new Random(); // 设置线条的颜色 g2.setColor(getRandColor(160, 200)); for (int i = 0; i < 20; i++) { int x = random.nextInt(w - 1); int y = random.nextInt(h - 1); int xl = random.nextInt(6) + 1; int yl = random.nextInt(12) + 1; g2.drawLine(x, y, x + xl + 40, y + yl + 20); } // 添加噪点 // 噪声率 float yawpRate = 0.05f; int area = (int) (yawpRate * w * h); for (int i = 0; i < area; i++) { int x = random.nextInt(w); int y = random.nextInt(h); // 获取随机颜色 int rgb = getRandomIntColor(); image.setRGB(x, y, rgb); } // 添加图片扭曲 shear(g2, w, h, c); g2.setColor(getRandColor(100, 160)); int fontSize = h - 4; Font font = new Font("Algerian", Font.ITALIC, fontSize); g2.setFont(font); char[] chars = code.toCharArray(); for (int i = 0; i < verifySize; i++) { AffineTransform affine = new AffineTransform(); affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2); g2.setTransform(affine); g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10); } g2.dispose(); ImageIO.write(image, "jpg", os); } /** * 随机颜色 * * @param fc * @param bc * @return */ private static Color getRandColor(int fc, int bc) { if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); } private static int getRandomIntColor() { int[] rgb = getRandomRgb(); int color = 0; for (int c : rgb) { color = color << 8; color = color | c; } return color; } private static int[] getRandomRgb() { int[] rgb = new int[3]; for (int i = 0; i < 3; i++) { rgb[i] = random.nextInt(255); } return rgb; } private static void shear(Graphics g, int w1, int h1, Color color) { shearX(g, w1, h1, color); shearY(g, w1, h1, color); } private static void shearX(Graphics g, int w1, int h1, Color color) { int period = random.nextInt(2); boolean borderGap = true; int frames = 1; int phase = random.nextInt(2); for (int i = 0; i < h1; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); g.copyArea(0, i, w1, 1, (int) d, 0); if (borderGap) { g.setColor(color); g.drawLine((int) d, i, 0, i); g.drawLine((int) d + w1, i, w1, i); } } } private static void shearY(Graphics g, int w1, int h1, Color color) { int period = random.nextInt(40) + 10; // 50; boolean borderGap = true; int frames = 20; int phase = 7; for (int i = 0; i < w1; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); g.copyArea(i, 0, 1, h1, 0, (int) d); if (borderGap) { g.setColor(color); g.drawLine(i, (int) d, i, 0); g.drawLine(i, (int) d + h1, i, h1); } } } }
-
-
实体类
-
User
package priv.dandelion.entity; public class User { private Integer id; private String username; private String password; public User(Integer id, String username, String password) { this.id = id; this.username = username; this.password = password; } public User() { } 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 getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
-
-
-
编码
-
Dao
-
UserMapper
package priv.dandelion.dao; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import priv.dandelion.entity.User; import java.util.List; public interface UserMapper { @Select("select * from tb_user where username = #{name}") public User selectByName(@Param("name") String name); }
-
-
Service
-
UserService
package priv.dandelion.service; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import priv.dandelion.dao.UserMapper; import priv.dandelion.entity.User; import priv.dandelion.utils.SqlSessionFactoryUtils; public class UserService { private SqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory(); public boolean selectUsername(String username) { // 获取SqlSession SqlSession sqlSession = factory.openSession(true); // 获取UserMapper UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 调用dao User user = mapper.selectByName(username); // 释放资源 sqlSession.close(); return user != null; } }
-
-
Controller - Servlet
-
用户名是否存在
package priv.dandelion.controller.servlet; import priv.dandelion.service.UserService; import priv.dandelion.utils.ReEncoding; 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; @WebServlet("/selectUsername") public class SelectUsernameServlet extends HttpServlet { UserService service = new UserService(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 接收用户名 String username = req.getParameter("username"); // 调用Service查询User boolean flag = service.selectUsername(username); // 响应标记 resp.getWriter().write("" + flag); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
-
展示验证码
package priv.dandelion.controller.servlet; import priv.dandelion.utils.CheckCodeUtil; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; /** * 验证码展示,并存储到Session */ @WebServlet("/checkCode") public class CheckCodeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 存储验证码图片到本地 // OutputStream fos = new FileOutputStream("d://check-code.jpg"); // 使用Response字节输出流获取验证码 ServletOutputStream outputStream = resp.getOutputStream(); String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, outputStream, 4); // 将验证码存储到Session中 HttpSession session = req.getSession(); session.setAttribute("checkCodeGenerate", checkCode); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
-
-
1.3.3 前端实现
-
步骤说明
- 给用户名输入框绑定光标失去事件
onblur
- 发送
Ajax
请求,携带username
参数 - 处理响应
- 给用户名输入框绑定光标失去事件
-
JS代码
-
检验用户名是否存在
// 给用户名输入框绑定发失去焦点事件 document.getElementById("username").onblur = function () { // 发送ajax请求 // 获取用户名的值 var username = this.value; // 创建核心对象 var xhttp; if (window.XMLHttpRequest) { xhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 xhttp = new ActiveXObject("Microsoft.XMLHTTP"); } // 建立连接 xhttp.open("GET", "http://localhost:8080/ajax-demo/selectUsername?username="+username); // 发送请求 xhttp.send(); // 获取响应 xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { //判断 if(this.responseText == "true"){ //用户名存在,显示提示信息,清除style中的display的none document.getElementById("username_err").style.display = ''; }else { //用户名不存在,清除提示信息,设置style中的display为none,不显示 document.getElementById("username_err").style.display = 'none'; } } }; }
-
验证码刷新
// 验证码刷新 document.getElementById("changeImg").onclick = function () { // 图片路径已经被缓存,需要在后面加参数进行刷新,为保证多次刷新,可以以时间毫秒数作为参数 document.getElementById("checkCodeImg").src = "/filter_demo/checkCode?"+new Date().getMilliseconds(); }
-
2、 Axios异步框架
Axios 对原生的AJAX进行封装,简化书写。
Axios官网是:
https://www.axios-http.cn
2.1 基本使用
-
引入Axios 的JavaScript文件
<script src="js/axios-0.18.0.js"></script>
-
发送请求并获取响应
axios({})
为发送请求部分,.then(function (resp){});
为回调函数,使用resp.data
可以获取到返回的结果-
get
axios({ method:"get", url:"http://localhost:8080/ajax-demo1/aJAXDemo1?username=zhangsan" }).then(function (resp){ alert(resp.data); })
-
post
axios({ method:"post", url:"http://localhost:8080/ajax-demo1/aJAXDemo1", data:"username=zhangsan" }).then(function (resp){ alert(resp.data); });
-
2.2 快速入门
2.2.1 后端实现
package priv.dandelion.controller.servlet;
import priv.dandelion.utils.ReEncoding;
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;
@WebServlet("/axiosDemo")
public class AxiosDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 响应数据
String username = req.getParameter("username");
resp.getWriter().write("hello "+ username + " axios get~");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
resp.getWriter().write("hello "+ username + " axios post~");
}
}
2.2.2 前端实现
<script src="js/axios-0.18.0.js"></script>
<script>
// // get
// axios({
// method: "get",
// url: "http://localhost:8080/ajax-demo/axiosDemo?username=zhangsan"
// }).then(function (resp) {
// alert(resp.data);
// })
// post
axios({
method: "post",
url: "http://localhost:8080/ajax-demo/axiosDemo",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
</script>
2.3 请求方法别名
-
为了方便起见, Axios 已经为所有支持的请求方法提供了别名。如下:
-
get
请求 :axios.get(url[,config])
-
delete
请求 :axios.delete(url[,config])
-
head
请求 :axios.head(url[,config])
-
options
请求 :axios.option(url[,config])
-
post
请求:axios.post(url[,data[,config])
-
put
请求:axios.put(url[,data[,config])
-
patch
请求:axios.patch(url[,data[,config])
-
-
get举例
axios.get("http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan").then(function (resp) { alert(resp.data); })
-
post举例
axios.post("http://localhost:8080/ajax-demo/axiosServlet","username=zhangsan").then(function (resp) { alert(resp.data); })
3、 JSON
概念:
JavaScript Object Notation
。JavaScript 对象表示法
3.1 概述
-
JSON和JS定义对象格式的区别
-
JavaScript
{ name:"zhangsan", age:23, city:"北京" }
-
JSON
{ "name":"zhangsan", "age":23, "city":"北京" }
-
3.2 JSON基础语法
3.2.1 定义格式
-
基本定义格式
var 变量名 = '{"key":value,"key":value,...}';
-
value的数据类型
- 数字(整数或浮点数)
- 字符串(使用双引号括起来)
- 逻辑值(true或者false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
-
value的数据类型示例
var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}'
3.2.2 代码格式
-
将JSON和JavaScript对象的相互转化
parse(str)
:将 JSON串转换为 js 对象。使用方式是:var jsObject = JSON.parse(jsonStr);
stringify(obj)
:将 js 对象转换为 JSON 串。使用方式是:var jsonStr = JSON.stringify(jsObject)
-
代码演示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSON-demo</title> </head> <body> <script> // 定义JSON字符串 var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}' alert(jsonStr); // 将 JSON 字符串转为 JS 对象 let jsObject = JSON.parse(jsonStr); alert(jsObject) alert(jsObject.name) // 将 JS 对象转换为 JSON 字符串 let jsonStr2 = JSON.stringify(jsObject); alert(jsonStr2) </script> </body> </html>
3.2.3 发送异步请求携带数据
- js 提供的
JSON
对象我们只需要了解一下即可。因为axios
会自动对 js 对象和JSON
串进行想换转换。- 发送异步请求时,如果请求参数是
JSON
格式,那请求方式必须是POST
。因为JSON
串需要放在请求体中。
-
使用
axios
发送请求时,如果要携带复杂的数据时都会以JSON
格式进行传递提前定义一个 js 对象,用来封装需要提交的参数,然后使用
JSON.stringify(js对象)
转换为JSON
串,再将该JSON
串作为axios
的data
属性值进行请求参数的提交var jsObject = {name:"张三"}; axios({ method:"post", url:"http://localhost:8080/ajax-demo/axiosServlet", data: JSON.stringify(jsObject) }).then(function (resp) { alert(resp.data); })
-
axios
会自动将 js 对象转换为JSON
串进行提交
var jsObject = {name:"张三"};
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:jsObject //这里 axios 会将该js对象转换为 json 串的
}).then(function (resp) {
alert(resp.data);
})
3.3 JSON串和Java对象的相互转换
Fastjson
是阿里巴巴提供的一个Java语言编写的高性能功能完善的JSON
库,是目前Java语言中最快的JSON
库,可以实现Java
对象和JSON
字符串的相互转换。
3.3.1 Fastjson 使用
-
依赖
<!-- JSON串和Java对象的相互转换 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
-
Java对象转JSON
String jsonStr = JSON.toJSONString(obj);
-
JSON转Java对象
User user = JSON.parseObject(jsonStr, User.class);
3.3.2 代码演示
public class FastJsonDemo {
public static void main(String[] args) {
// 将Java对象转为JSON字符串
User user = new User();
user.setId(1);
user.setUsername("zhangsan");
user.setPassword("123");
String jsonString = JSON.toJSONString(user);
System.out.println(jsonString);//{"id":1,"password":"123","username":"zhangsan"}
// 将JSON字符串转为Java对象
User u = JSON.parseObject("{\"id\":1,\"password\":\"123\",\"username\":\"zhangsan\"}", User.class);
System.out.println(u);
}
}
4、 案例
4.1 需求和方案
-
需求
- 使用AJAX+JSON完成品牌数据的查询和添加
-
方案
-
查询所有
- 页面加载完成后发送异步请求,获取数据列表
- controller调用service进行查询
- service查询到List集合,返回给controller
- controller将List转换为JSON
- 将JSON串使用Response写回页面
- 遍历字符串数据,展示表格
-
添加数据
-
点击添加按钮跳转到添加信息页面
-
输入内容点击提交,将数据封装为JSON格式,使用AJAX发送异步请求到后端
-
后端接收数据后反序列化JSON数据
由于前端提交的是 json 格式的数据,所以我们不能使用
request.getParameter()
方法获取请求参数- 如果提交的数据格式是
username=zhangsan&age=23
,后端就可以使用request.getParameter()
方法获取 - 如果提交的数据格式是 json,后端就需要通过 request 对象获取输入流,再通过输入流读取数据
- 如果提交的数据格式是
-
将获取到的JSON对象转换为Brand对象
-
调用Service的add()发明合法添加数据
-
将JSON数据返回给浏览器
-
-
4.2 查询所有功能
4.2.1 环境准备
-
SQL
-- 删除tb_brand表 drop table if exists tb_brand; -- 创建tb_brand表 create table tb_brand ( -- id 主键 id int primary key auto_increment, -- 品牌名称 brand_name varchar(20), -- 企业名称 company_name varchar(20), -- 排序字段 ordered int, -- 描述信息 description varchar(100), -- 状态:0:禁用 1:启用 status int ); -- 添加数据 insert into tb_brand (brand_name, company_name, ordered, description, status) values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0), ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1), ('小米', '小米科技有限公司', 50, 'are you ok', 1);
-
实体类
package priv.dandelion.entity; public class Brand { // id 主键 private Integer id; // 品牌名称 private String brandName; // 企业名称 private String companyName; // 排序字段 private Integer ordered; // 描述信息 private String description; // 状态:0:禁用 1:启用 private Integer status; public Brand() { } public Brand(Integer id, String brandName, String companyName, Integer ordered, String description, Integer status) { this.id = id; this.brandName = brandName; this.companyName = companyName; this.ordered = ordered; this.description = description; this.status = status; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getBrandName() { return brandName; } public void setBrandName(String brandName) { this.brandName = brandName; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } public Integer getOrdered() { return ordered; } public void setOrdered(Integer ordered) { this.ordered = ordered; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } @Override public String toString() { return "Brand{" + "id=" + id + ", brand_name='" + brandName + '\'' + ", company_name='" + companyName + '\'' + ", ordered=" + ordered + ", description='" + description + '\'' + ", status=" + status + '}'; } }
-
MyBatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--起别名--> <typeAliases> <package name="priv.dandelion.entity"/> </typeAliases> <environments default="development"> <environment id="development"> <!-- 采用JDBC的事务管理方式 --> <transactionManager type="JDBC"/> <!-- 数据库连接信息 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- value的值一定不能换行,一定!一定!不能换行 --> <property name="url" value="jdbc:mysql:///db1?useSSL=false&useUnicode=true&characterEncoding=utf-8&useServerPrepStmts=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 扫描mapper,加载SQL映射文件 --> <mappers> <package name="priv.dandelion.dao"/> </mappers> </configuration>
-
Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="priv.dandelion.dao.BrandMapper"> <!-- 解决数据库与实体类命名不一致问题 --> <resultMap id="brandResultMap" type="brand"> <result column="brand_name" property="brandName"></result> <result column="company_name" property="companyName"></result> </resultMap> </mapper>
-
Mapper接口
package priv.dandelion.dao; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.ResultMap; import org.apache.ibatis.annotations.Select; import priv.dandelion.entity.Brand; import java.util.List; public interface BrandMapper { @ResultMap("brandResultMap") @Select("select * from tb_brand") List<Brand> selectAll(); @Insert("insert into tb_brand " + "values(null, #{brandName}, #{companyName}, " + "#{ordered}, #{description}, #{status})") void addBrand(Brand brand); }
-
工具类 - 获取SqlSession工厂
package priv.dandelion.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class SqlSessionFactoryUtils { // 提升作用域,用于再方法内进行返回 private static SqlSessionFactory sqlSessionFactory; // 静态代码块会随着类的加载自动执行且只执行一次 static { String resource = "mybatis-config.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } public static SqlSessionFactory getSqlSessionFactory() { return sqlSessionFactory; } }
-
Service
package priv.dandelion.service; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import priv.dandelion.dao.BrandMapper; import priv.dandelion.entity.Brand; import priv.dandelion.utils.SqlSessionFactoryUtils; import java.util.List; public class BrandService { SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); public List<Brand> selectAll() { SqlSession sqlSession = sqlSessionFactory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); List<Brand> brands = mapper.selectAll(); sqlSession.close(); return brands; } public void addBrand(Brand brand) { SqlSession sqlSession = sqlSessionFactory.openSession(true); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); mapper.addBrand(brand); sqlSession.close(); } }
4.2.2 后端实现
-
Servlet
package priv.dandelion.controller.servlet; import com.alibaba.fastjson.JSON; import priv.dandelion.entity.Brand; import priv.dandelion.service.BrandService; 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.util.List; @WebServlet("/selectAll") public class SelectAllServlet extends HttpServlet { BrandService service = new BrandService(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 调用service 查询 List<Brand> brands = service.selectAll(); // 将集合序列化为JSON数据 String jsonString = JSON.toJSONString(brands); // 响应数据 // 处理中文数据 resp.setContentType("text/json;charset=utf-8"); resp.getWriter().write(jsonString); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
4.2.3 前端实现
-
引入JS文件
<script src="js/axios-0.18.0.js"></script>
-
监听监听页面加载完成事件,在页面加载完成后发送AJAX异步请求
// 当页面加载完成后发送AJAX请求 window.onload = function () { // 发送AJAX请求 axios({ method:"get", url:"http://localhost:8080//brand-demo-ajax/selectAll" }).then(function (resp) { // 获取数据 let brands = resp.data; // 表格数据 let tableData = "<tr>\n" + " <th>序号</th>\n" + " <th>品牌名称</th>\n" + " <th>企业名称</th>\n" + " <th>排序</th>\n" + " <th>品牌介绍</th>\n" + " <th>状态</th>\n" + " <th>操作</th>\n" + " </tr>"; for (let i = 0; i < brands.length; i++) { let brand = brands[i]; // 对表格数据进行拼接 tableData += "<tr align=\"center\">\n" + " <td>" + (i + 1) + "</td>\n" + " <td>" + brand.brandName + "</td>\n" + " <td>" + brand.companyName + "</td>\n" + " <td>" + brand.ordered + "</td>\n" + " <td>" + brand.destination + "</td>\n" + " <td>" + brand.status + "</td>\n" + "\n" + " <td><a href=\"#\">修改</a> <a href=\"#\">删除</a></td>\n" + " </tr>" } // 设置表格对象 document.getElementById("brandTable").innerHTML = tableData; }) }
-
整体页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <a href="addBrand.html"><input type="button" value="新增"></a><br> <hr> <table id="brandTable" border="1" cellspacing="0" width="100%"> </table> <script src="js/axios-0.18.0.js"></script> <script> // 当页面加载完成后发送AJAX请求 window.onload = function () { // 发送AJAX请求 axios({ method:"get", url:"http://localhost:8080//brand-demo-ajax/selectAll" }).then(function (resp) { // 获取数据 let brands = resp.data; // 表格数据 let tableData = "<tr>\n" + " <th>序号</th>\n" + " <th>品牌名称</th>\n" + " <th>企业名称</th>\n" + " <th>排序</th>\n" + " <th>品牌介绍</th>\n" + " <th>状态</th>\n" + " <th>操作</th>\n" + " </tr>"; for (let i = 0; i < brands.length; i++) { let brand = brands[i]; // 对表格数据进行拼接 tableData += "<tr align=\"center\">\n" + " <td>" + (i + 1) + "</td>\n" + " <td>" + brand.brandName + "</td>\n" + " <td>" + brand.companyName + "</td>\n" + " <td>" + brand.ordered + "</td>\n" + " <td>" + brand.destination + "</td>\n" + " <td>" + brand.status + "</td>\n" + "\n" + " <td><a href=\"#\">修改</a> <a href=\"#\">删除</a></td>\n" + " </tr>" } // 设置表格对象 document.getElementById("brandTable").innerHTML = tableData; }) } </script> </body> </html>
4.3 添加品牌功能
4.3.1 后端实现
-
Servlet部分
package priv.dandelion.controller.servlet; import com.alibaba.fastjson.JSON; import priv.dandelion.entity.Brand; import priv.dandelion.service.BrandService; 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.BufferedReader; import java.io.IOException; @WebServlet("/add") public class AddBrandServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BrandService service = new BrandService(); // 前端发送为JSON数据,不能使用request.getParameter接收数据 // 接收前端发送的JSON数据 BufferedReader reader = req.getReader(); String params = reader.readLine(); // 将JSON字符串反序列化为Java对象 Brand brand = JSON.parseObject(params, Brand.class); // 调用Service进行添加 service.addBrand(brand); // 响应成功标识 resp.getWriter().write("success"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
4.3.2 前端实现
-
导入JS
<script src="js/axios-0.18.0.js"></script>
-
给提交按钮绑定单击事件,并发送AJAX异步请求到服务器
// 给按钮绑定单击事件 document.getElementById("btn").onclick = function () { // 将表单转换为JSON串 var formData = { brandName:"", companyName:"", ordered:"", description:"", status:"", }; // 获取表单数据 formData.brandName = document.getElementById("brandName").value; formData.companyName = document.getElementById("companyName").value; formData.description = document.getElementById("description").value; formData.ordered = document.getElementById("ordered").value; // 获取单选的较为特殊,和单选的性质有关 let status = document.getElementsByName("status"); for (let i = 0;i < status.length; i++) { if (status[i].checked) { formData = status[i].value; } } alert(formData); // 发送AJAX请求 axios({ method:"post", url:"http://localhost:8080//brand-demo-ajax/add", data:formData }).then(function (resp) { alert(resp.data); // 判断响应数据是否为success if (resp.data == "success") { // 重定向到查询所有页面 location.href = "http://localhost:8080//brand-demo-ajax/brand.html"; } }) }
-
完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加品牌</title> </head> <body> <h3>添加品牌</h3> <form action="" method="post"> 品牌名称:<input id="brandName" name="brandName"><br> 企业名称:<input id="companyName" name="companyName"><br> 排序:<input id="ordered" name="ordered"><br> 描述信息:<textarea rows="5" cols="20" id="description" name="description"></textarea><br> 状态: <input type="radio" name="status" value="0">禁用 <input type="radio" name="status" value="1">启用<br> <input type="button" id="btn" value="提交"> </form> <script src="js/axios-0.18.0.js"></script> <script> // 给按钮绑定单击事件 document.getElementById("btn").onclick = function () { // 将表单转换为JSON串 var formData = { brandName:"", companyName:"", ordered:"", description:"", status:"", }; // 获取表单数据 formData.brandName = document.getElementById("brandName").value; formData.companyName = document.getElementById("companyName").value; formData.description = document.getElementById("description").value; formData.ordered = document.getElementById("ordered").value; // 获取单选的较为特殊,和单选的性质有关 let status = document.getElementsByName("status"); for (let i = 0;i < status.length; i++) { if (status[i].checked) { formData = status[i].value; } } alert(formData); // 发送AJAX请求 axios({ method:"post", url:"http://localhost:8080//brand-demo-ajax/add", data:formData }).then(function (resp) { alert(resp.data); // 判断响应数据是否为success if (resp.data == "success") { // 重定向到查询所有页面 location.href = "http://localhost:8080//brand-demo-ajax/brand.html"; } }) } </script> </body> </html>
额外说明:
该部分代码略显臃肿,但是该示例的目的主要是对AJAX、Axios以及JSON相关知识点的整理和演示,后续通过前端框架可以大大简化该部分代码。