AJAX技术
浏览器是多进程的,简单的说就是,浏览器每打开一个标签页,就相当于创建了一个独立的浏览器进程。但是js是基于单线程的,而这个线程就是浏览器的js引擎,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序。如果需要实现多线程文件操作,则需要依赖js中Work类来实现。
JavaScript作为一门客户端的脚本语言,主要的任务是处理用户的交互,而用户的交互无非就是响应DOM的增删改,使用事件队列的形式,一次事件循环只处理一个事件响应,使得脚本执行相对连续。如果JS引擎被设计为多线程的,那么DOM之间必然会存在资源竞争,那么语言的实现会变得非常臃肿,在客户端跑起来,资源的消耗和性能将会是不太乐观的,故设计为单线程的形式,并附加一些其他的线程来实现异步的形式,这样运行成本相对于使用JS多线程来说降低了很多。
问题的引入:校验用户名是否重复
@Controller
public class UserController{
@GetMapping("/add")
public String add(Model model){
User user=new User(); model.addAttribute("user",user);
return "users/add"; 打开输入页
}
}
输入页面 thymeleaf
<form th:action="@{/add}" method="post" th:object="${user}">
<input name="username" th:field="*{username}"/>
...还有很多的输入域....
</form>
如果需要用户名称唯一,则需要填充完所有的输入域后,点击提交按钮才能将数据发送到服务器端,由
Controller调用Service进行用户名称的存在性判断。这里实际很麻烦,因为仅仅是一个用户名的问题,提供了很多的数据
解决方案1:
输入用户名后点击一个额外的连接,进行用户名称的验证
<form>
<input name="username" th:field="*{username}" /><a href="">验证用户名</a>
... ...
</form>
问题1是如何生成响应页面,问题2是由于采用的是同步处理机制,所以在服务器端接收并处理数据的过程中,客户端只能处于等待状态
目前所采用的请求/响应的同步处理会导致一种不连续的客户体验
解决方案2:Ajax的交互方式
可以将不连续的客户体验转换为对客户比较友好的连续的客户体验
同步和异步
举个例子:普通B/S模式(同步) AJAX技术(异步)
- 同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
- 异步: 请求通过事件触发->服务器处理(这时浏览器仍然可以作其他事情)->处理完毕
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式
易懂的理解: - 异步传输: 你传输吧,我去做我的事了,传输完了告诉我一声
- 同步传输: 你现在传输,我要亲眼看你传输完成,才去做别的事
什么是Ajax
AJAX是异步的JavaScript和XML,传输的是XML的格式数据,可以通过JavaScript的XMLHttpRequest对象来进行数据交互,Ajax包括:
- XHTML和CSS进行显示
- 使用文档对象模型DOM(Document Object Model)作动态显示和交互
- 使用XML和XSLT做数据交互和操作
- 使用XMLHttpRequest进行异步数据接收
- 使用JavaScript将它们绑定在一起
理解 - Ajax一种不用刷新整个页面便可与服务器通讯的办法
AJAX编程模型
用户名称的唯一性校验
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
var xhr=null; //由于在多个方法中使用,所以定义为页面全局
function ff(){
//1、实例化xhr对象---XMLHttpRequest,不管使用什么浏览器,只要经过认证的浏览器都
会包含这个组件。不需要额外安装
createXHR();//由于浏览器的差异性,所以构建xhr对象的具体方法不完全一致
if(xhr){
//2、定义请求回调事件处理
//获取用户输入的数据
let nn=document.getElementById('username').value;
xhr.open('GET','exists?name='+nn,true);//打开服务器的连接,参数1是请求方
法,参数2是请求地址URL,参数3是否采用异步请求
xhr.onreadystatechange=callback;//注册回调事件处理,当特定事件发生时
【readystate发生变化时】,会自动执行这里注册的方法
//3、发送请求
xhr.send();//由于采用的是get请求,所以没有协议体,是采用请求URL地址传递数据
}else{
alert('您的浏览器不支持异步操作!');
}
}
function createXHR(){
//浏览器的实现差异主要体现在IE10以前的版本和非IE浏览器两大类
if(window.ActiveXObject){ //判断窗口对象中是否有ActiveXObject组件,从而判断
IE浏览器
xhr=new ActiveXObject('Microsoft.XMLHttp'); //实际上不同版本的浏览器参
数不同
} else{ //针对非IE浏览器,例如FireFox、Chrome等
xhr=new XMLHttpRequest();
}
}
function callback(){
//在一个异步请求处理过程中,XMLHttpRequest的readyState有0-4共5种状态,4表示处理
完毕,并接收到服务器的响应信息
var ss=document.getElementById('show');
if(4==xhr.readyState){
if(200==xhr.status){ //status是服务器的响应状态码,例如404、500等,200表
示处理正确
//处理响应信息 XMLHttpRequest对象中有2个属性可以用于接收服务器的响应信息,
responseText将响应内容当作文本进行处理,responseXML将响应内容当作一个xml文档进行处理
let tt=xhr.responseText;
//使用DOM进行页面的部分更新
ss.innerHTML=tt;
} else {
ss.innerHTML='请求出现问题!';
}
}else{
ss.innerHTML='数据正在处理中....'
}
}
</script>
</head>
<body>
<form th:action="@{/add}" method="post" th:object="${user}">
<!-- onblur用于定义输入域失去焦点事件,当input:username失去焦点时则立即触发ff函数的
执行; onfocus获取焦点事件 -->
<input name="username" id="username" th:field="*{username}"
placeholder="请输入用户名称" onblur="ff()"/><span id="show"></span><br/>
<input name="password" id="password" th:field="*{password}"
placeholder="请输入口令"/>
</form>
</body>
</html>
对应的控制器,一般不会在SpringMVC中使用response生成响应内容。注意AJAX是客户端技术,和浏览器相关,但是和所使用的服务器技术无关
@GetMapping("/exists")
public void exists(String name,HttpServletResponse response) throws
IOException {
System.out.println("exists.....");
String msg="";
if("yanjun".equals(name))
msg="<font color=red>用户名称已经被占用</font>";//偷懒,因为这种写法在
html5中不允许
else
msg="<font color=blue>用户名称可以使用</font>";
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.println(msg);
out.close();
}
具体开发过程中可以使用浏览器的开发者工具进行js代码的调试
特点
传统的web应用允许用户填写表单form,当提交表单时就向web服务器发送一个请求。服务器接收并处理传来的表单,然后返回一个新的网页。这个做法浪费了许多带宽,因为在前后2个页面中的大部分HTML代码往往是相同的。
优势
AJAX应用可以仅向服务器发送并取回必需的数据,并在客户端采用JavaScript处理来自服务器的响应
缺点
- AJAX大量使用了Javascript和AJAX引擎,而这个取决于浏览器的支持。IE5.0及以上、Mozilla1.0、
NetScape7及以上版本才支持,Mozilla虽然也支持AJAX,但是提供XMLHttpRequest的方式不一
样。所以,使用AJAX的程序必须测试针对各个浏览器的兼容性 - AJAX更新页面内容的时候并没有刷新整个页面,因此网页的后退功能是失效的;有的用户还经常搞不清楚现在的数据是旧的还是已经更新过的。这个需要在明显位置提醒用户“数据已更新”
- 对流媒体的支持没有FLASH、Java Applet好
- 一些手持设备(如手机、PDA等)现在还不能很好的支持Ajax
区别 - 与传统的WEB应用不同, AJAX采用异步交互过程。AJAX在用户与服务器之间引入一个中间媒介,从而消除了网络交互过程中的处理—等待—处理—等待缺点。AJAX引擎用JavaScript语言编写,通常藏在一个隐藏的框架中,它负责编译用户界面及与服务器之间的交互
- 因为在服务器和浏览器之间交换的数据大量减少(大约只有原来的5%),结果就能看到响应更快的应用。同时很多的处理工作可以在发出请求的客户端上完成,所以Web服务器的处理时间也减少了
- 使用Ajax的最大优点就是能在不刷新整个页面的前提下维护数据,这使得Web应用程序更为迅捷地响应用户交互,并避免了在网络上发送那些没有改变的信息
总结 - Ajax在本质上是一个浏览器端的技术,和具体的服务器端技术无关
- Ajax技术之主要目的在于局部交换客户端及服务器间之数据
- 这个技术的主角XMLHttpRequest的最主要特点,在于能够不用重新载入整个版面来更新资料,Refresh without Reload
- 与服务器之间的沟通,完全是透过Javascript来实现
- 使用XMLHttpRequest本身传送的数据量很小,所以反应会更快,也就让网络应用更像一个桌面程序
AJAX引擎允许用户与应用软件之间的交互过程异步进行,独立于用户与网络服务器间的交流
用Javascript调用AJAX引擎来代替产生一个HTTP的用户动作,像内存中的数据编辑、页面导航、数据校验这些不需要重新载入整个页面的需求都可以交给AJAX来执行
AJAX工作原理
Ajax的核心是JavaScript对象XMLHttpRequest
xhr对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XMLHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户
AJAX采用异步交互过程。AJAX在用户与服务器之间引入一个中间媒介xhr,从而消除了网络交互过程中的处理—等待—处理—等待缺点
用户的浏览器在执行任务时即装载了AJAX引擎。引擎用JavaScript语言编写,通常藏在一个隐藏的框架中。它负责编译用户界面及与服务器之间的交互
AJAX引擎允许用户与应用软件之间的交互过程异步进行,独立于用户与网络服务器间的交流。可用Javascript调用AJAX引擎来代替产生一个HTTP的用户动作,内存中的数据编辑、页面导航、数据校验这些不需要重新载入整个页面的需求可以交给AJAX来执行
XMLHttpRequest
使用XMLHttpRequest可以用 JavaScript 发出到服务器的请求,并在不阻塞用户的情况下处理响应
在创建 Web 站点并用 XMLHttpRequest 在客户机浏览器上无刷新地执行屏幕更新的同时,它还提供了更多灵活性和丰富的用户体验。XMLHttpRequest应用程序的示例包括Google 的 Gmail 服务、Google的 Suggest 动态查询界面以及 MapQuest 的动态地图界面
没有一样东西在不同的浏览器上得到同样的结果。针对不同的浏览器创建xhr对象
function createXHR(){
//浏览器的实现差异主要体现在IE10以前的版本和非IE浏览器两大类
if(window.ActiveXObject){ //判断窗口对象中是否有ActiveXObject组件,从而判断
IE浏览器
xhr=new ActiveXObject('Microsoft.XMLHttp'); //实际上不同版本的浏览器参数
不同
} else{ //针对非IE浏览器,例如FireFox、Chrome等
xhr=new XMLHttpRequest();
}
}
xhr常用方法
- abort()停止当前请求
- getAllResponseHeaders() 作为字符串返回完整的headers
- getResponseHeader(“headerLabel”) 作为字符串返回单个的header
- open(“method”,“URL”[,asyncFlag[,“userName”[, “password”]]])设置未决的请求的目标URL,方法和其他参数。主要使用的方法样例为open(“GET”,“/bb.do?id=123”,true)
- send(content) 发送请求
- setRequestHeader(“label”, “value”) 设置header并和请求一起发送
get请求
get请求使用URL传递请求参数
let nn=document.getElementById('username').value; //获取输入框中用户输入的数据
xhr.open('GET','exists?name='+nn,true);//打开服务器的连接,参数1是请求方法,参数2是
请求地址URL,参数3是否采用异步请求。这里利用URL传递请求参数?name=abcd
xhr.onreadystatechange=callback;//注册回调事件处理,当特定事件发生时【readystate发生
变化时】,会自动执行这里注册的方法
//发送请求
xhr.send();//由于采用的是get请求,所以没有协议体,是采用请求URL地址传递数据
post请求
post请求实际上就是模拟form表单的post提交
xhr.open('POST','exists',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.onreadystatechange=callback;
xhr.send('name='+nn+'&age=18');
xhr属性
- onreadystatechange注册状态改变的事件触发器,当xhr的readyState发生变化时自动回调的函数
- readyState是xhr对象状态值,属于integer类型,不同的状态值表示不同的状态。0=未初始化、1=读取中、2=已读取、3=交互中、4 =完成。实际上使用onreadystatechange不可能获取到5种状态值
- responseText服务器进程返回数据的文本版本,事实上不管服务器生成的内容,都会当作文本字符串进行处理
- responseXML服务器进程返回数据的兼容DOM的XML文档对象
-
- 当前的具体开发种一般不载使用xml进行数据传输,建议使用 JSON 格式
- status服务器返回的状态码,如404=文件未找到、200 =“成功”
- statusText服务器返回的状态文本信息,例如返回内容为200 OK
注册回调事件
onreadystatechange事件:当请求状态发生变化,即readyState的值发生变化的时候激发的事件
xhr.onreadystatechange=callback;
注意:这个函数名称不加括号,不指定参数。也可用Js函数直接量方式定义响应函数。如XMLHttpReq.onreadystatechange = function() { };
- onreadystatechange属性接收一个EventListener值,保证在事件触发时激活该对象
- 指定当服务器返回信息时客户端的处理方式。只要将相应的处理函数名称赋给XMLHttpRequest对象的onreadystatechange属性就可以了
事件处理函数
function callback(){
//在一个异步请求处理过程中,XMLHttpRequest的readyState有0-4共5种状态,4表示处理完
毕,并接收到服务器的响应信息
var ss=document.getElementById('show');
if(4==xhr.readyState){
if(200==xhr.status){ //status是服务器的响应状态码,例如404、500等,200表示处理
正确
//处理响应信息 XMLHttpRequest对象中有2个属性可以用于接收服务器的响应信息,
responseText将响应内容当作文本进行处理,responseXML将响应内容当作一个xml文档进行处理
let tt=xhr.responseText;
ss.innerHTML=tt;
} else {
ss.innerHTML='请求出现问题!';
}
}else{
ss.innerHTML='数据正在处理中....'
}
}
改进
AJAX 虽然可以实现无刷新更新页面内容,但是也不是什么地方都可以用,主要应用在交互较多、频繁读
数据、数据分类良好的Web 应用中
开发步骤:AJAX实质上也是遵循Request/Server模式,所以这个框架基本的流程是:
- 对象初始化
- 发送请求
- 服务器接收
- 服务器返回
- 客户端接收
- 修改客户端页面内容
只不过这个过程是异步的
Javascript对象编程
为了简化AJAX编程,可以利用js的对象编程将繁琐的步骤进行封装
function AjaxObject(){
this.xhr=null;
this.getXMLHttpRequest=function (){
if(window.ActiveXObject){
this.xhr=new ActiveXObject("Microsoft.XMLHttp");
} else {
this.xhr=new XMLHttpRequest();
}
};
//url是请求地址,必须的;params是请求参数,格式应该为key=value&key=value;
HttpMethod是请求方法
this.sendRequest=function(url,params,HttpMethod){
//如果调用时没有传递HttpMethod参数,则默认请求方法为post
if(!HttpMethod)
HttpMethod='POST';
//创建XMLHttpRequest对象
this.getXMLHttpRequest();
if(!this.xhr) {
//建立和服务器的连接
this.xhr.open(HttpMethod,url,true);
//注册回调事件处理函数
this.xhr.onreadystatechange=this.callback;
if('POST'==HttpMethod){
this.xhr.setRequestHeader('Content-Type','application/x-www-
form-urlencoded');
}
//发送请求
this.xhr.send(params);
} else {
alert('您的浏览器不支持异步操作!');
}
};
this.callback=function(){};
}
模板化操作已经定义在AjaxObject中,页面中需要使用则仅仅进行引入即可
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../static/jslib/ajax.js" th:src="@{/jslib/ajax.js}">
</script>
</head>
<body>
<form action="#" th:action="@{/add}" method="post" th:object="${user}">
<input name="username" id="username" th:field="*{username}"
onblur="ff()"/>
<span id="errusername"></span><br/>
<input name="password" type="password" th:field="*{password}"/>
</form>
<script>
function ff() {
//获取用户在<input id=username/>中的输入数据
var uname = document.getElementById('username').value;
var errname = document.getElementById('errusername');
console.log(errname);
if (uname != '') {
var obj = new AjaxObject();
//覆盖定义AjaxObject中的callback函数
obj.callback = function () {
console.log(errname);
if (4 == obj.xhr.readyState) {
if (200 == obj.xhr.status) {
let ss = obj.xhr.responseText;
console.log(ss);
errname.innerHTML = ss;
}
}
}
obj.sendRequest('exists?username=' + uname);
} else {
errname.innerHTML = '请输入用户名称';
}
}
</script>
</body>
</html>
使用JQuery工具框架
直接使用js编程比较麻烦,而且还必须考虑浏览器的差异性。【浏览器差异性的解决方案】
为了简化javascript的开发,一些javascript库诞生了。当今流行的javascript库有:jQuery诞生于2005年,Dojo、 EXT_JS、DWR、YUI…
jQuery是John Resig在2006年初创建的,对使用Javascript开发进行简化的框架(库、官方定义),能解决DOM脚本和Ajax开发中重复的工作
- 其宗旨是: Write Less,Do More
- 它是轻量级的js库(压缩后只有21k),这是其他js库所不及的!它兼容各大浏览器(甚至是怪异的IE6!,JQuery1)
- jQuery是一个挺好的轻量级的JS框架,能帮助程序员快速的开发JS应用,并在一定程度上改变了编写JS代码的习惯
JQuery基本使用
首先需要导入jQuery类库,也可以使用webjars的方式由maven进行管理
- 可以下载jQuery类库,并在本地导入
-
- 下载时有2个版本,一个是mini版一般用于生产环境,文件体积较小,没有提示信息,甚至没有换行;一个是开发版,其中包含有日志显示和提示信息,所以开发中一般使用开发版
-
- 官方网站 jquery.com
- 在连网状态下,直接导入远程jQuery类库
<script src="jslib/jquery-3.6.3.js"></script>
<p id="show">
飞雪连天射白鹿<br/>
笑书神夏一臂猿<br/>
</p>
<script>
window.onload=function(){
$('#show')
.slideUp('slow') //slideUp() 方法用于向上滑动元素
.slideDown('slow'); //slideDown() 方法用于向下滑动元素
}
</script>
用途
- 解决浏览器兼容性问题:兼容CSS3,还兼容各种浏览器IE 6.0+, FireFox1.5+, Safari 2.0+, Opera9.0+等。【如果需要使用较为古老的浏览器则需要考虑所使用的jquery版本】
- 使用户能更方便地处理HTML文档、事件、实现动画效果,并且方便地为网站提供Ajax交互
<script>$(function(){alert(“test”);})</script>
window.onload用于定义页面加载完毕则执行回调函数 window.οnlοad=function(){}
( f u n c t i o n ( ) ) 实际上是 (function(){})实际上是 (function())实际上是(document).ready(function(){})的缩写,表示的是在当前页面的DOM加载完毕则立即执行的回调函数<p id="show"> 飞雪连天射白鹿<br/> 笑书神夏一臂猿<br/> </p> <button id="btn1">显示</button> <script> $(function (){ $('#show').hide(); //隐藏指定id=show的元素 $('#btn1').click(function(){ //给id=btn1添加点击事件处理函数 $('#show').show(); //显示id=show的元素 }); }); </script> 区别: window.onload或者使用 <body onload=""> 定义页面加载>完毕后执行的回调函数,页面加载完毕不等于渲染显示>完毕,这个函数只能定义一个,如果>多次定义则最后一次定义生效,其他无效 jquery中的$(function())或者>$(document).ready(function(){})用于定义DOM加载完毕后执行的回调函数,可以定义多次,都会执行
在编程过程中经常会使用定位器用于查找指定的页面元素,基础语法 $(expression,[context]) 接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组元素。jQuery 的核心功能都是通过这个函数实现的
功能
- 获取页面的部分内容
<p id="p1">一段文本信息</p>
<buton id="btn1">获取显示文本内容</buton>
<script>
$(function(){
$('#btn1').click(function(){
alert($('#p1').html()); //获取id=p1中的innerHTML内容
});
});
</script>
- 修改页面的外观
<p id="p1">一段文本信息</p>
<buton id="btn1">获取显示文本内容</buton>
<script>
$(function(){
$('#btn1').click(function(){
$('#p1').css('background-color','red');
});
});
</script>
- 修改页面的内容
<p id="p1">一段文本信息</p>
<buton id="btn1">获取显示文本内容</buton>
<script>
$(function(){
$('#btn1').click(function(){
$('#p1').html('新的显示内容'); //如果使用html()则是获取内容,如果使用
html(参数)则是修改内容,对应innerHTML
});
});
</script>
- 在页面中响应用户的交互
还提供改进基本的 JavaScript 结构,如迭代和数组操作。实际上将更改编写JS代码的方式
- 给页面加上动画
show/hide显示或者隐藏,slidexxx滑动显示
- 无刷新返回服务器端的信息
. a j a x ( ) 功能较为丰富的 a j a x 函数, .ajax()功能较为丰富的ajax函数, .ajax()功能较为丰富的ajax函数,.get或者$.post等
- 还提供改进基本的 JavaScript 结构,如迭代和数组操作。实际上将更改编写JS代码的方式
<script>
$(document).ready(function(){
$("button").click(function(){ //通过标签名称定为页面元素,查找所有的
button标签;给button添加click点击事件处理函数
$("li").each(function(){ //获取所有的li标签,.each循环遍历每个
li元素
alert($(this).text()) $(this)获取被遍历的当前元素,text()
获取内部的文本信息,类似于innerText
});
});
});
</script>
<button>输出每个列表项的值</button>
<ul>
<li>Coffee</li>
<li>Milk</li>
<li>Soda</li>
</ul>
AJAX应用
$.get(url,[data],[callback],[datatype]) 其中参数URL发送请求的 URL字符串;data可选的,发送给服务器的字符串或key/value键值对;callback可选的,请求成功后执行的回调函数;dataType可选的,从服务器返回的数据类型。默认智能猜测(可以是xml、json、script或html)
执行用户名称的唯一性验证【半截版本】
页面
<form action="#" method="post">
<input id="username" name="username"/><span id="err"></span><br/>
<input type="password"/>
</form>
<script src="jslib/jquery-3.6.3.js"></script>
<script>
$(function () {
$('#username').blur(function () {// 给id=username的元素上添加
onblur=function(){}
//获取数据,可以使用CSS定位器查找元素,也可以使用this
var uname = $('#username').val(); //相当于于针对元素调用value,获取
用户输入的数据
if (uname == '') {
$('#err').html('<font color=red>用户名称不能为空!</font>')
}else{
$.get('exists',{username:uname},function(data){参数3是回调函
数,如果需要获取响应状态值,则可以添加一个参数
$('#err').html(data); //将获取到Controller的响应内容写入
id=err的元素
});
}
});
});
</script>
控制器
@Controller
public class UserController {
@ResponseBody // 如果没有添加这个注解,返回的String是逻辑地址名
@GetMapping("/exists")
public String exists(String username){
String msg="";
if("yanjun".equals(username))
msg="<font color=red>用户名称已经被占用</font>";
else
msg="<font color=blue>用户名称可以使用</font>";
return msg;
}
}
控制器
$.post( URL [, data ] [, callback ] [, dataType ] ) 其中参数URL发送请求的 URL字符串;data可选的,发送给服务器的字符串或key/value键值对;callback可选的,请求成功后执行的回调函数;dataType可选的,从服务器返回的数据类型。默认智能猜测。
页面定义
<input id="username" name="username"/><span id="err"></span><br/>
<input type="password"/><br/>
<button id="btn1">提交数据</button>
<script src="jslib/jquery-3.6.3.js"></script>
<script>
$(function () {
$('#username').blur(function () {// 给id=username的元素上添加οnblur=function(){}
//获取数据,可以使用CSS定位器查找元素,也可以使用this
var uname = $('#username').val(); //相当于针对元素调用value,获取用户输入的数据
if (uname == '') {
$('#err').html('<font color=red>用户名称不能为空!</font>')
}else{
$.get('exists',{username:uname},function(data){
$('#err').html(data);
});
}
});
});
$(function(){
$('#btn1').click(function(){
$.post('add',
{username:$('#username').val(),password:$('input:password').val()},function(
data,status){
console.log(data);
console.log(status);
});
});//使用post向服务器段发送数据
});
</script>
服务器端接受到了数据
@Controller
public class UserController {
@ResponseBody
@GetMapping("/exists")
public String exists(String username){
String msg="";
if("yanjun".equals(username))
msg="<font color=red>用户名称已经被占用</font>";
else
msg="<font color=blue>用户名称可以使用</font>";
return msg;
}
@ResponseBody
@PostMapping("/add")
public String add(String username,String password){
System.out.println(username+"-->"+password);
return "success";
}
}
在$.post的回调函数中获取到了controller方法的字符串success,完全可以在 js 代码中根据返回类型进行页面的跳转 【前后端分离开发的基础】
$.post('add',
{username:$('#username').val(),password:$('input:password').val()},
function(data){
console.log(data);
if("success"==data)
location.href='001.html';
else
location.href='002.html';
});
$.ajax({name:value, name:value, … })
常见的属性配置值
- url规定发送请求的 URL。默认是当前页面
- type规定请求的类型(GET 或 POST),默认使用GET提交。
- async布尔值,表示请求是否异步处理。默认是 true。
- data规定要发送到服务器的数据
- dataType预期的服务器响应的数据类型
- error(xhr,status,error)如果请求失败要运行的函数
- success(result,status,xhr)当请求成功时运行的函数。
样例代码
一般的用法
<script>
$(document).ready(function(){
$("button").click(function(){
$.ajax({url:"demo.txt",async:true,success:function(result){
$("div").html(result);
}});
});
});
</script>
出错处理
$(document).ready(function(){
$("button").click(function(){
$.ajax({url:"wrongfile.txt",error:function(xhr){
alert("错误提示: " + xhr.status + " " + xhr.statusText);//通过xhr
获取响应状态
}});
});
});
如果提交参数过多,可以方法$('#form-id').serialize()
最常见的操作格式
$.ajax({
url:"save",
data: $("form").serialize(),
dataType: "json", //声明期望返回类型为json,则success对应的函数中的result就是
一个JSON对象
success:function(data){
if(data.success==true)
alert("添加成功!");
window.location="list";
}else{
alert("添加失败!");
}
},
});
在具体的开发中使用单一字符串返回数据内容比较少,所以具体开发中一般会自定义一个JsonResult类,其中封装所需要返回的数据,最后在Controller方法上添加@ResponseBody使其以json格式返回
@Data
public class JsonResult implements Serializable {
private int code; //人为自定义的响应状态码,不是http规范中的响应状态码
private boolean success;//用于表示处理是否成功
private String message; //响应的提示信息
private Object data;//响应数据
}
控制器
@Controller
public class UserController {
@ResponseBody
@GetMapping("/exists")
public String exists(String username){
String msg="";
if("yanjun".equals(username))
msg="<font color=red>用户名称已经被占用</font>";
else
msg="<font color=blue>用户名称可以使用</font>";
return msg;
}
@ResponseBody
@PostMapping("/add")
public JsonResult add(String username, String password){
System.out.println(username+"-->"+password);
return JsonResult.success("新增用户成功");
}
}
页面中使用ajax
<form id="frm1">
<input id="username" name="username"/><span id="err"></span><br/>
<input type="password"/><br/>
</form>
<button id="btn1">提交数据</button>
<script src="jslib/jquery-3.6.3.js"></script>
<script>
$(function () {
$('#username').blur(function () {// 给id=username的元素上添加
onblur=function(){}
//获取数据,可以使用CSS定位器查找元素,也可以使用this
var uname = $('#username').val(); //相当于于针对元素调用value,获取
用户输入的数据
if (uname == '') {
$('#err').html('<font color=red>用户名称不能为空!</font>')
} else {
$.get('exists', {username: uname}, function (data) {
$('#err').html(data);
});
}
});
});
$(function () {
$('#btn1').click(function () {
$.ajax({
url: 'add',
type:'POST',
data:$('#frm1').serialize(),
dataType:'json',
success:function(res){
console.log(res);
if(res.success){
alert(res.message);
location.href='001.html';
}
}
});
});
});
</script>
JQuery默认的Serialize和SerializeArray可以把form表单的数据进行序列化,这里只是key=value&key1=value1 的格式,不是JSON。
- $(“#formID”).serialize() 将表单内容序列化成一个字符串。这样在ajax提交表单数据时,就不用一一列举出每一个参数。只需将data参数设置为 $(“form”).serialize() 即可
- var jsonData = $(“#formID”).serializeArray() 可以将页面表单序列化成一个JSON结构的对象。注意不是JSON字符串。比如 [{“name”:“yanjun”},{…}] 获取数据为 jsonData[0].name
GET和POST方法的区别
- 发送的数据数量。在GET中,只能发送有限数量的数据,因为数据是在URL中发送的。在POST中,可以发送大量的数据,因为数据是在正文主体中发送的。
- 安全性。GET方法发送的数据不受保护,因为数据在URL栏中公开,这增加了漏洞和黑客攻击的风险。POST方法发送的数据是安全的,因为数据未在 URL 栏中公开,还可以在其中使用多种编码技术,这使其具有弹性。
- 加入浏览器的历史记录中。GET查询的结果可以加入书签中,因为它以URL的形式存在;而POST查询的结果无法加入书签中。
- 编码。在表单中使用GET方法时,数据类型中只接受ASCII字符。在表单提交时,POST方法不绑定表单数据类型,并允许二进制和ASCII字符。
- 可变大小。GET方法中的可变大小约为2000个字符;POST方法理论上说是没有上限的,一般最多允许8Mb的可变大小。
- 缓存。GET方法的数据是可缓存的,而POST方法的数据是无法缓存的。
- 主要作用。GET方法主要用于获取信息。而POST方法主要用于更新数据。
JSON格式
JSON即 JS 对象简谱是一种轻量级的数据交换格式。它基于 ECMAScript欧洲计算机协会制定的js规范的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
具体语法格式
- 数组是由方括号括起来的一组值构成,具体格式为[元素1,元素2,…],如[3, 1, 4, 1, 5, 9, 2, 6]
- 对象由花括号括起来的逗号分割的成员构成,成员是字符串键和对应的值由逗号分割的键值对组成,如 {“name”: “John Doe”, “age”: 18, “address”: {“country” : “china”, “zip-code”: “10000”}}
json处理方法1
对于服务器返回的JSON字符串,如果jquery异步请求没做类型说明,或者以字符串方式接受,那么需要做一次对象化处理,方式不是太麻烦,就是将该字符串放于eval()中执行一次。这种方式也适合以普通javascipt方式获取json对象
控制器,返回内容为一个字符串类型
@RestController //当前类中的所有请求处理方法的返回值都是String或者json字串
public class TestController {
@GetMapping("/list")
public String list(){
StringBuilder sb=new StringBuilder("{");
sb.append("id:").append(100).append(",");
sb.append("username:'").append("xixixi").append("',");
sb.append("password:'").append("666666").append("'}");
return sb.toString();
}
}
html页面中接收返回类型,并使用eval函数将字符串转换为json对象
<script>
$(function(){
$.ajax({
url:'list',
success:function(res){
var obj=eval("("+res+")");
console.log(obj);
console.log(obj.id+"---"+obj.username+"---"+obj.password);
}
})
});
</script>
json处理方法2
jQuery提供了方法parseJSON,这需要一个标准的JSON字符串,并返回生成的JavaScript对象
- 处理思路实际上和eval函数调用是一致的,只是jQuery提供了安全的调用
- 要求返回的字符串必须是正规标准的 JSON 字符串
控制器定义
@RestController //当前类中的所有请求处理方法的返回值都是String或者json字串
public class TestController {
@GetMapping("/list")
public String list(){
StringBuilder sb=new StringBuilder("{");
sb.append("\"id\":").append(100).append(",");
sb.append("\"username\":").append("\"xixixi\"").append(",");
sb.append("\"password\":").append("\"666666\"").append("}");
return sb.toString();
}
}
页面调用
<script>
$(function(){
$.ajax({
url:'list',
success:function(res){
var obj=$.parseJSON(res);
console.log(obj);
console.log(obj.id+"---"+obj.username+"---"+obj.password);
}
})
});
</script>
json处理方法3 [推荐]
对于服务器返回的JSON字符串,如果jquery异步请求将type设为 json,或者利用$.getJSON()方法获得服务器返回,那么就不需要eval方法了,因为这时候得到的结果已经是json对象了,只需直接调用该对象即可
控制器
@RestController //当前类中的所有请求处理方法的返回值都是String或者json字串
public class TestController {
@GetMapping("/list")
public List<User> list(){
List<User> res=new ArrayList<>();
for(int i=0;i<10;i++){
User tmp=new User();
tmp.setId(1L+i);
tmp.setUsername("name_"+i);
tmp.setPassword(i+"_pwd");
res.add(tmp);
}
return res;
}
}
在页面中获取返回的数据
<script>
$(function(){
$.ajax({
url:'list',
dataType:'json',
success:function(res){
$.each(res,function(ind,obj){
console.log(ind+"--
>"+obj.username+":"+obj.id+":"+obj.password)
});
}
})
});
</script>
前后端分离
SpringMVC
在方法上添加@ResponseBody
- 不会跳转页面
- 如果返回值满足key-value形式,例如对象或map,则把响应头设置为application/json,把转换后的内容输出流形式响应给客户端
- 如果返回值不满足key-value,例如返回值为String,则把响应头设置为text/html,把方法返回值以流的形式直接输出
@RestController 相当于@Controller+@ResponseBody,在类上标注@RestController,此类会被Spring 识别为提供Rest API的控制器 - 如果方法返回String ,响应的Content-Type类型为text/html
- 如果方法返回的是Object,例如Map,响应的Content-Type类型为application/json
事实上底层使用jackson进行json转换,所以在项目中应该要导入jackson的jar包。如果希望切换为ali的fastjson则不要额外配置
ORM框架
ORM对象关系映射是一种面向对象编程语言中的对象和数据库中的数据之间的映射
Sun公司推出了操作持久层数据库的规范API用于统一ORM各种框架,这套规范是JPA
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用
极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展
依赖:spring-boot-starter-data-jpa
public interface UserDao extends JpaRepository<User, Long> {
}
@OneToMany(mappedBy="customer",cascade=CascadeType.ALL)
@ManyToOne
@JoinColumn(name = "blog_fk")
private BlogInfo blog;
@ManyToMany
@JoinTable(name = "PostAndTag",
joinColumns = @JoinColumn(name = "postId", referencedColumnName
= "id"),
inverseJoinColumns = @JoinColumn(name = "tagId",
referencedColumnName = "id"))
private List tags;
REST的核心是定义一个包含与客户端进行交互资源的系统。 这些资源以超媒体驱动的方式实现。
Spring Data JPA是基于Spring Data 的Repository之上,可以将Repository自动输出为REST资源。目前Spring Data REST支持将Spring Data JPA、Spring Data MongoDB、Spring Data Neo4j、Spring Data
Gemfire以及Spring Data Cassandra的Repository自动转换成REST服务
依赖:spring-boot-starter-data-rest
通过在Controller中引入Repository来对外提供REST API。Spring Boot还可以通过spring-boot-starter-
data-rest来对外提供REST API,可以免于编写对应的Controller,且具备分页和排序的功能
@RepositoryRestResource
public interface AuthorRepository extends PagingAndSortingRepository<Author,
Long> {
}
启动应用程序,并访问http://localhost:8080/authors
添加用户 POST: http://localhost:8081/users {“username”: “yanjun”,“password”:
“123456”, “age”: 18}
删除用户 DELETE : http://localhost:8081/users/1
根据id查询 GET : http://localhost:8081/users/3
分页查询 GET : http://localhost:8081/users?page=0&size=1&sort=age,desc
定制查询
public interface UserDao extends JpaRepository<User, Long> {
List<User> findUsersByUsernameContaining(@Param("username")String
username);
}
访问http://localhost:8081/users/search查询自定义接口,使用路径http://localhost:8081/users/search/findUsersByUsernameContaining{?username} 调用方法
public interface UserDao extends JpaRepository<User, Long> {
//rel表示接口查询中,这个方法的key;path表示请求路径
@RestResource(rel = "auth", path = "auth")
User findByUsernameAndPassword(@Param("name") String username,
@Param("pwd") String password);
}
调用 GET : http://localhost:8081/users/search/auth?name=summerday&pswd=123456
Postman是一个支持REST的客户端,用它来测试REST资源,postman有浏览器中的插件形式的和客户端的,可以使用客户端来测试
设置接口对前端隐藏
@RestResource(exported = false)
public void deleteById(Long aLong);
@RepositoryRestResource设置rest请求路径。通过匹配路径中的参数完成对数据库的访问。配合JPA使用
@RepositoryRestResource(collectionResourceRel = "users",itemResourceRel =
"u",path = "user")
public interface UserDao extends JpaRepository<User, Long> {
}
- 上下文路径配置 spring.data.rest.base-path=/api
- path是访问路径
依赖:spring-boot-starter-hateoas
超媒体作为应用状态引擎HATEOAS是实现REST规范的一种原则,返回数据中带有相应的资源链接,通过一个资源就可以得到从这个资源可以访问的其他的资源,就像是一个访问到一个页面,可以再通过这个页面去访问其他的页面一样。它最大的意义在于只需要访问一个资源就可以遍历所有的资源。基本返回数据中除了content包含数据user信息之外,还有一个_links属性表示和该数据user相关的资源链接URL地址
Richardson提出的REST成熟度模型
- 第一个层次Level 0的Web服务只是使用 HTTP 作为传输方式,实际上只是远程方法调用RPC的一种具体形式。SOAP 和 XML-RPC 都属于此类。
- 第二个层次Level 1的Web服务引入了资源的概念,每个资源有对应的标识符和表达。
- 第三个层次Level 2的Web服务使用不同的HTTP方法来进行不同的操作,并且使用HTTP状态码来表示不同的结果。
- 第四个层次Level 3的Web服务使用HATEOAS。在资源的表达中包含了链接信息。客户端可以根据链接来发现可以执行的动作。
对应持久化支持spring-boot-starter-data-jpa
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=mysq
定义实体类
@Data
@Entity
@Table(name = "tb_users")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 32,nullable = false,unique = true)
private String username;
@Column(length = 32,nullable = false)
private String password;
}
定义对应的Repository
public interface UserRepository extends JpaRepository<User,Long> {
@Modifying
@Query("delete from User b where b.username=:username")
void deleteBooks(@Param("username") String username);
}
用于构建HATEOAS的响应,直接使用Spring HATEOAS提供的实现类更加方便;而且多数情况下可以满足需求。对于由单个对象或概念支持的资源EntityModel;集合的资源可以使用CollectionModel
- linkTo可以通过methodOn创建指向控制器方法的指针。提交一个虚拟的方法调用结果
- withRel()入参为String或者LinkRelation实例,LinkRelation用于定义链接关系的接口。可用于实现基于spec的链接关系以及自定义链接关系
- withSelfRel()使用默认的self Link关系创建当前构建器实例构建的{@link Link}
- methodOn()创建控制器类的代理,该代理类记录方法调用,并将其公开在为方法的返回类型创建的代理中。这使得我们想要获得映射的方法能够流畅表达。但是,使用此技术可以获得的方法受到一些限制:
-
- 返回类型必须能够代理,因为需要公开对其的方法调用
-
- 传递给方法的参数通常被忽略,通过引用的参数除外@PathVariable,因为它们组成了URI
@Controller
public class UserController {
@Autowired
private UserRepository userRepository;
@RequestMapping("/users/{id}")
public ResponseEntity<EntityModel<User>> findOne(@PathVariable("id")
Long id) {
return userRepository.findById(id).map(user->EntityModel.of(user,
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(
UserController.class).findOne(user.getId())).withSelfRel(),
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(
UserController.class).findAll()).withRel("users")))
.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@GetMapping("/users")
public ResponseEntity<CollectionModel<EntityModel<User>>> findAll() {
List<EntityModel<User>>
users=StreamSupport.stream(userRepository.findAll()
.spliterator(),false).map(user-
>EntityModel.of(user,WebMvcLinkBuilder
.linkTo(WebMvcLinkBuilder.methodOn(UserController.class)
.findOne(user.getId())).withSelfRel(),
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(
UserController.class).findAll()).withRel("users")))
.collect(Collectors.toList());
return ResponseEntity.ok(CollectionModel.of(users,
WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(UserController.class)
.findAll()).withSelfRel()));
}
}
生成的响应为 {“_embedded”:{“userList”:
[{“id”:1,“username”:“yanjun”,“password”:“123456”,“_links”:{“self”:
{“href”:“http://localhost:8080/users/1”},“users”:
{“href”:“http://localhost:8080/users”}}},
{“id”:2,“username”:“yan1111”,“password”:“123456”,“_links”:{“self”:
{“href”:“http://localhost:8080/users/2”},“users”:
{“href”:“http://localhost:8080/users”}}}]},“_links”:{“self”:
{“href”:“http://localhost:8080/users”}}
依赖:spring-restdocs-mockmvc
Spring REST Docs是一个为Spring项目生成API文档的框架,它通过在单元测试中额外添加API信息描述,从而自动生成对应的文档片段。
@WebMvcTest
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
public class HelloControllerTests {
private MockMvc mockMvc;
@BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation))
.build();
}
@Test
public void testHello() throws Exception {
mockMvc.perform(get(“/hello”).param(“name”, “ScienJus”))
.andExpect(status().isOk())
.andExpect(jsonPath(“msg”, “Hello ScienJus!”).exists())
.andDo(document(“hello”));
}
}