Ajax&json
Ajax
Ajax介绍
Asynchronous JavaScript And XML(异步的 JavaScript 和 XML)
Ajax可以在不刷新页面的前提下,进行页面局部更新
Ajax是前端开发的必备技能,也是主流的开发模式
Ajax使用流程
创建XmlHttpRequest对象
发送Ajax请求
处理服务器响应



因为默认端口号是80,所以可以不写。
1-创建XMLHttpRequest对象
XMLHttpRequest是Ajax的核心,Ajax使用该对象发起请求、接收响应
XMLHttpRequest并不是W3C的标准,不同浏览器的创建方式不同
1 2 3 4 5 6 7 8 9 10 11
| var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
|


2-发送Ajax请求
xmlhttp.open()用于创建请求
xmlhttp.send()用于发送请求
1 2 3 4
| xmlhttp.open("GET","http://loaclhost/test?name=admin");
xmlhttp.send();
|




3-处理服务器响应
| readyState值 |
说明 |
| readyState = 0 |
请求未初始化 |
| readyState = 1 |
服务器连接已建立 |
| readyState = 2 |
请求已被接收 |
| readyState = 3 |
请求正在处理 |
| readyState = 4 |
响应文本已被接收 |





JSON
JSON介绍
JavaScript Object Notation(JavaScript对象表示法)
JSON 是轻量级的文本数据交换格式
JSNO 独立于语言,具有自我描述性,更易理解
1 2 3 4 5 6 7
| { "site":[ {"name":"Github","url":"www.github.com"}, {"name":"百度","url":"www.baidu.com"}, {"name":"网易","url":"www.163.com"} ] }
|
JSON语法规则
数据由键(key)/值(value)描述,由冒号分隔
大括号代表一个完整的对象,拥有多个键/值对
中括号保存数组,多个对象之间使用逗号分隔
利用JSON保存员工数据



JavaScript操作JSON

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> var employeeList = [ { "empno": 7369, "ename": "李宁", "job": "软件工程师", "hiredate": "2019-5-12", "salary": 13000, "dname": "研发部" }, { "empno": 7499, "ename": "王乐", "job": "客户经理", "hiredate": "2017-4-22", "salary": 10000, "dname": "市场部", "customers": [ { "cname": "李东" }, { "cname": "刘楠" } ] } ]; for (var i = 0;i < employeeList.length; i++) { var employee = employeeList[i]; console.log(employee); } </script> </head> <body>
</body> </html>
|





Ajax应用实践
Ajax+JSON开发模式

Jackson
Jackson是国内外著名的Java开源JSON序列化组件
Jackson国内拥有大量使用者,拥有API简单,效率等优点
Jackson也是众多Java框架的底层组件,掌握Jackson很重要


利用Jackson实现JSON序列化输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @WebServlet("/news") public class NewsServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<News> list = new ArrayList<>(); list.add(new News("TIOBE:2020年编程语言排行趋势","2020-5-1","TIOBE","...")); list.add(new News("TIOBE:2021年编程语言排行趋势","2021-5-1","TIOBE","...")); list.add(new News("TIOBE:2022年编程语言排行趋势","2022-5-1","TIOBE","...")); list.add(new News("TIOBE:2023年编程语言排行趋势","2023-5-1","TIOBE","...")); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(list); resp.setContentType("text/json;charset=utf-8"); resp.getWriter().println(json); } }
|

记得往lib中加入下好的依赖

Ajax处理JSON数据

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="container"></div> <script> var xmlhttp; if(window.XMLHttpRequest){ xmlhttp = new XMLHttpRequest(); }else{ xmlhttp = new ActiveXObject("microsoft.XMLHTTP"); } xmlhttp.open("GET", "/news"); xmlhttp.send(); xmlhttp.onreadystatechange = function (){ if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ var str = xmlhttp.responseText; console.log(str); } } </script> </body> </html>
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="container"></div> <script> var xmlhttp; if(window.XMLHttpRequest){ xmlhttp = new XMLHttpRequest(); }else{ xmlhttp = new ActiveXObject("microsoft.XMLHTTP"); } xmlhttp.open("GET", "/news"); xmlhttp.send(); xmlhttp.onreadystatechange = function (){ if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ var str = xmlhttp.responseText; console.log(str); var json = JSON.parse(str); console.log(json); } } </script> </body> </html>
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="container"></div> <script> var xmlhttp; if(window.XMLHttpRequest){ xmlhttp = new XMLHttpRequest(); }else{ xmlhttp = new ActiveXObject("microsoft.XMLHTTP"); } xmlhttp.open("GET", "/news"); xmlhttp.send(); xmlhttp.onreadystatechange = function (){ if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ var str = xmlhttp.responseText; console.log(str); var json = JSON.parse(str); console.log(json); for (var i = 0;i < json.length ; i++) { var news = json[i]; var container = document.getElementById("container"); container.innerHTML = container.innerHTML + news.title; } } } </script> </body> </html>
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <script> var xmlhttp; if(window.XMLHttpRequest){ xmlhttp = new XMLHttpRequest(); }else{ xmlhttp = new ActiveXObject("microsoft.XMLHTTP"); } xmlhttp.open("GET", "/news"); xmlhttp.send(); xmlhttp.onreadystatechange = function (){ if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ var str = xmlhttp.responseText; console.log(str); var json = JSON.parse(str); console.log(json); for (var i = 0;i < json.length ; i++) { var news = json[i]; var container = document.getElementById("container"); container.innerHTML = container.innerHTML + "<h2>" + news.title + "</h2>"; } } } </script>
|

Axios快速入门
https://www.axios-http.cn/

使用 unpkg CDN:
1
| <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|

Axios发送Get请求



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/js/axios.js"></script> </head> <body> <div id="container"></div> <script> axios.get('/news',{}).then(function (response){ console.log(response); var json = response.data; for (var i = 0;i < json.length ; i++) { var news = json[i]; var container = document.getElementById("container"); container.innerHTML = container.innerHTML + "<h2>" + news.title + "</h2>"; } }).catch(function (error){ console.log(error); }) </script> </body> </html>
|



或者写成
1
| axios.get('/news?t=pypl').then(function (response)
|


Axios发送Post请求
在axios.html中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/js/axios.js"></script> </head> <body> <div id="container"></div> <script> axios.post('/news', "t=pypl&l=abc", {headers:{"content-type":"application/x-www-form-urlencoded"}}) .then(function (response){ console.log(response); var json = response.data; for (var i = 0;i < json.length ; i++) { var news = json[i]; var container = document.getElementById("container"); container.innerHTML = container.innerHTML + "<h2>" + news.title + "</h2>"; } }) .catch(function (error){ console.log(error); }); </script> </body> </html>
|

发现405错误,原因是没写doPost方法。


说明传递成功。

解决405,在里面追加doPost方法。

扩展:
请求参数过多的话开源采用面向对象方法


Ajax同步与异步的区别
同步是在服务器未返回JSON前,JS程序一直处于阻塞等待的状态
异步是在服务器未返回JSON前,不阻塞程序,Ajax通过回调获取结果

创建和news基础上改动的async.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="container"></div> <script> var xmlhttp; if(window.XMLHttpRequest){ xmlhttp = new XMLHttpRequest(); }else{ xmlhttp = new ActiveXObject("microsoft.XMLHTTP"); } xmlhttp.open("GET", "/news?t=pypl"); xmlhttp.send(); console.log("请求数据已发送"); xmlhttp.onreadystatechange = function (){ if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ var str = xmlhttp.responseText; console.log(str); var json = JSON.parse(str); console.log(json); for (var i = 0;i < json.length ; i++) { var news = json[i]; var container = document.getElementById("container"); container.innerHTML = container.innerHTML + "<h2>" + news.title + "</h2>"; } } } </script> </body> </html>
|
newsServlet类中,睡眠5s后进行以下代码



第三个参数设为false表示关闭异步,开启同步。

只能显示出日志

将onchange下面的内容提到日志下面就可以同步进行

开发时基本上用的是异步。
实现二级联动菜单



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @WebServlet("/channel") public class ChannelServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String level = req.getParameter("level"); String parent = req.getParameter("parent"); List<Channel> chlist = new ArrayList<>(); if (level.equals("1")){ chlist.add(new Channel("ai","人工智能")); chlist.add(new Channel("web","前端开发")); } else if (level.equals("2")) { if (parent.equals("ai")){ chlist.add(new Channel("dl","深度学习")); chlist.add(new Channel("cv","计算机视觉")); chlist.add(new Channel("nlp","自然语言处理")); } else if (parent.equals("web")) { chlist.add(new Channel("html","HTML超文本标记语言")); chlist.add(new Channel("css","CSS极联样式表")); chlist.add(new Channel("js","JavaScript脚本")); } } ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(chlist); resp.setContentType("application/json;charset=utf-8"); resp.getWriter().println(json); } }
|










onchange为复选框选择事件

再把数据填充到二级下拉框中。



但选择前端开发时出现bug

再选择回来,发生叠加。

所以每次加载都要清除原来的数据

Java正则表达式
正则表达式基础语法
正则表达式介绍
什么是正则表达式
正则表达式是检查、匹配字符串的表达式
正则表达式是描述规则,主流语言都有良好支持
字符串校验、查找与替换是正则表达式主要使用场景
正则表达式案例
检查输入的身份证号是否合法(15位、18位)
示例:13010220200328091X
正则表达式:
1
| (^\d{15}$)|(^\d{18}&)|(^\d{17}(\d|X|x)$)
|
字符范围匹配

在线表达式测试工具:https://tool.oschina.net/regex
eg:精准匹配hello或hallo
匹配数字范围(0570-0579)
单选题只允许输入ABCD其中一项
元字符

eg:
请匹配数字(3213.383219)
匹配杭州与宁波座机号码(0571|0574-XXXXXXXX)
1
| 057[14]-\d\d\d\d\d\d\d\d
|
请匹配18位身份证号
1
| [1234568]\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d[0-9xX]
|
多次重复匹配

eg:
验证短信验证码 (321456)
请匹配全国座机号(区号3或者4位-电话号码7或8位)
请匹配英文姓名(例如:James Watson)
1
| [A-Z][a-z]{1,}\s[A-Z][a-z]{0,}
|
1
| [A-Z][a-z]+\s[A-Z][a-z]*
|
定位匹配

.*是指任意长度的字符
eg:匹配ab开头ab结尾的字符串
贪婪模式
在满足条件的情况下尽可能多匹配到字符串
示例:111222333 正则:\d{6,8}
匹配结果:11122233
非贪婪模式
在满足条件的情况下尽可能少匹配字符串
示例:111222333 正则:\d{6,8}?
匹配结果:111222
eg:提取超链接里的网址
1
| <a href="www.baidu.com">百度</a><a href="www.sina.com">新浪</a>
|
贪婪正则:

非贪婪正则:

表达式分组
分组将”正则”分组为多个子表达式
示例:abababcdcdcd
正则表达式:(ab){3}(cd){3}
eg:
匹配验证码(4位或6位)
匹配车牌号(粤D-U888G)
1
| ^([冀黑粤晋][A-Z])-([A-Z0-9]{5})$
|
匹配中文或英文姓名
James Watson
张三
再混合
应用实例
JavaScript表单验证




过滤器
Filter过滤器基础
初识过滤器-Filter

过滤器-Filter
过滤器(Filter)是J2EE Servlet模块下的组件
Filter的作用是对URI进行统一的拦截处理
Filter通常用于应用程序层面进行请求的前置处理
过滤链

开发第一个过滤器
开发过滤器三要素
任何过滤器都要实现javax.servlet.Filter接口
在Filter接口的doFilter()方法中编写过滤器的功能代码
在web.xml中对过滤器进行配置,定义拦截URI的范围











如果将filterChain.doFilter()注释掉,可以看到,控制台有输出,但页面没有输出



访问/hello也是同样效果
过滤器生命周期


开启tomcat时就进行。

重启Tomcat时销毁。
过滤器的特性
过滤器对象在Web应用启动时被创建且全局唯一
唯一的过滤器针对性在并发环境中采用”多线程”提供服务
过滤器应用实践
WebFilter注解的使用
过滤器配置形式
1 2 3 4 5 6 7 8
| <filter> <filter-name>firstFilter</filter-name> <filter-class>org.example.filter.FistFilter</filter-class> </filter> <filter-mapping> <filter-name>firstFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
|
过滤器注解形式
1 2 3 4
| @WebFilter(filterName="annotationFilter", urlPatterns="/*") public class AnnotationFilter implements Filter { ... }
|


配置与注解如何选择
配置形式维护性更好,适合应用全局过滤
注释形式开发体验更好,适用于小型项目敏捷开发
开发字符集过滤器
Web中文乱码的解决
GET请求-server.xml增加URIEncoding=”UTF-8”
POST请求-使用request.setCharacterEncoding(“UTF-8”)
响应-response.setContentType(“text/html;charset=utf-8”)
由于每次可能都要设置以上内容,所以有了字符集过滤器来便利。
ServletRequest接口




1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CharacterEncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; req.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=utf-8"); filterChain.doFilter(servletRequest, servletResponse); }
@Override public void destroy() {
} }
|



这样,我们在开发时就不用再注意去加上编码。
参数化过滤器
过滤器参数化
过滤器为了增强灵活性,允许配置信息放在web.xml
在web.xml中配置设置过滤器参数
优化字符集过滤器






用注释形式的话(了解):

url-pattern常用写法
/index.html - 执行资源精准匹配
/servlet/* - 以前缀进行模糊匹配
*.html - 以后缀进行模糊匹配
但不支持前缀加后缀




后缀匹配:

前缀匹配:



/与/*的区别
/ 应用在Servlet配置,使该Servlet替代主页
/* 应用在过滤器,代表对所有请求拦截



原本的index.html失效了。

过滤链的开发技巧
过滤链

过滤链开发注意事项
每一个过滤器应具有单独职能
过滤器的执行顺序以为准
调用chain.doFilter()将请求向后传递





如果在Filter中忘了写
1
| filterChain.doFilter(servletRequest, servletResponse);
|
的话:



杜绝了后面过滤器的执行。

注解形式是按filterName中字母顺序执行的,这样会降低对程序的维护性。
利用过滤器实现多端匹配
多端设备自动匹配



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class DeviceAdapterFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; String uri = req.getRequestURI(); / String userAgent = req.getHeader("user-agent"); String targetUri = null; if (userAgent.toLowerCase().indexOf("android") != -1 || userAgent.toLowerCase().indexOf("iphone") != -1){ targetUri = "/mobile" + uri; System.out.println("移动端正在访问,重新跳转URI:" + targetUri); }else { targetUri = "/desktop" + uri; System.out.println("PC端正在访问,重新跳转URI:" + targetUri); } resp.sendRedirect(targetUri); }
@Override public void destroy() {
} }
|
web.xml
1 2 3 4 5 6 7 8 9
| <filter> <filter-name>deviceAdaptFilter</filter-name> <filter-class>org.example.filter.DeviceAdapterFilter</filter-class> </filter> <filter-mapping> <filter-name>deviceAdaptFilter</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
|
此时运行,访问index.html
会出现错误,多次重定向。

要再加上一个前置判断

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class DeviceAdapterFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {
} @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; String uri = req.getRequestURI(); String userAgent = req.getHeader("user-agent"); String targetUri = null; if (uri.startsWith("/desktop") || uri.startsWith("/mobile")) { filterChain.doFilter(servletRequest, servletResponse); } else { if (userAgent.toLowerCase().indexOf("android") != -1 || userAgent.toLowerCase().indexOf("iphone") != -1) { targetUri = "/mobile" + uri; System.out.println("移动端正在访问,重新跳转URI:" + targetUri); } else { targetUri = "/desktop" + uri; System.out.println("PC端正在访问,重新跳转URI:" + targetUri); } resp.sendRedirect(targetUri); } }
@Override public void destroy() {
} }
|
此时输入http://localhost:8080/index.html

按F12进入手机模式,再输入http://localhost:8080/index.html


监听器(了解)
监听器-Listener
监听器(Listener)是J2EE Servlet模块下的组件
Listener的作用对Web应用对象的行为进行监控
通过Listener监听自动触发指定的功能代码
过滤器与监听器的区别
过滤器(Filter)的职责是对URL进行过滤拦截,是主动的执行
监听器(Listener)的职责是对Web对象进行监听,是被动触发
三种监听对象
ServletContext - 对全局ServletContext及其属性进行监听
HttpSession - 对用户会话及其属性操作进行监听
ServletRequest - 对请求及属性操作进行监听

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class WebListener implements ServletContextListener, HttpSessionListener, ServletRequestListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("ServletContext object has been created"); }
@Override public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("ServletContext object has been destroyed"); }
@Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { System.out.println("Request destroyed"); }
@Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { System.out.println("Request created"); }
@Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { System.out.println("Session created"); }
@Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { System.out.println("Session destroyed"); } }
|
web.xml
1 2 3
| <listener> <listener-class>org.example.WebListener</listener-class> </listener>
|

重启时



而Session会话监听会在getSession()时触发,但销毁会在服务器关闭的30分钟后才进行。
监听器在现在已经用得很少了,了解一下即可。