目录
3.1.3 前端 JavaScript 发送 Ajax 请求的代码
一. 异步请求举例
例子一:
比如用户注册完毕之后点击了提交按钮提交表单,浏览器会发送HTTP请求将用户填写的数据提交到后端服务器;而用户在注册的时候,填写用户名时,输入一个用户名,有时候会显示该用户名已被占用,请选择另外一个,那么它是怎么知道这个用户名已经存在的了呢?
其实就是通过异步请求来完成的,当用户输入完毕用户名之后,它会在不影响用户使用的情况下不用刷新网页向服务器发送异步HTTP请求,向数据库表中查询当前用户名是否已经存在。
如果返回值为NULL,则说明该用户名尚未被占用;
如果返回值不为NULL,则说明该用户名已被其他用户占用,不能使用此用户名;
例子二:
比喻电商网站,我们会发现,页面上的数据都是在不断变化的,热销商品在不断变化,那么它是如何做到的呢?
其实也可以通过异步请求来完成,用户在浏览商品的时候,页面的后台在不断的向服务器发送HTTP请求,查询数据库中最新的商品数据,返回给前端,前端浏览器解析返回的数据在展示给用户。
二. Ajax 异步请求详解
2.1 Ajax 简介
现在来看,Ajax 并不是一个很新的技术,但是它的异步请求思想还是直接我们去借鉴和思考的。现在的很多前端框架,归根结底还是封装的 JavaScript 代码,Ajax 也不例外。实质上,Ajax 只是 JQuery 框架封装的一个函数,也可以称之为 "$.ajax()" 函数,通过调用 ajax() 函数加载发送异步请求,JavaScript 提供了一个对象XHR(XMLHttpResponse),它就是 Ajax 的核心对象,发送请求以及接收服务器数据的返回全靠它。
此外,现代浏览器全都内置了 XMLHttpResponse 对象,所以都支持的 XMLHttpResponse 对象数据。
说白了就一句话:Ajax 是一种异步无刷新发送网络请求的技术。
2.2 XMLHttpResponse对象的属性和方法
(1)XMLHttpResponse 属性
属性 | 描述 |
status | 返回请求的状态号200:"OK" 403:"Forbidden" 404:"Not Found" |
statusText | 返回状态文本(比如"OK"或"Not Found") |
responseText | 以字符串返回响应数据 |
responseXML | 以 XML 数据返回响应数据 |
readyState | 保存 XMLHttpResponse 的状态。 0:请求未初始化; 1:服务器连接已建立; 2:请求已收到; 3:正在处理请求; 4:请求已完成且响应已就绪; |
onreadystatechange | 定义当 readyState 属性变化发生变化时被调用的函数 |
(2)XMLHttpResponse 方法
方法 | 描述 |
send() | 向服务器发送GET请求 |
send(String) | 向服务器发送POST请求 |
open(method,url,async,users,psw) | method:规定请求的方法; url:请求路径GET和POST; user:可选的用户名称; psw:可选的密码; |
sendRequestHeader() | 向要发送的报头添加标签/值对 |
abort() | 取消当前请求 |
getAllResponseHeaders() | 返回头部信息 |
getResponseHeader() | 返回特定的头部信息 |
2.3 ajax() 的使用方式
ajax() 函数的使用方式就是直接写 "$.ajax(参数...)",参数需要传递一个方法体;
众所周知,JavaScript 方法体如下代码所示
function(){
函数体...
}
所以 ajax() 函数的语法如下
$.ajax(function(){
函数体...
})
但是,ajax 需要我们传入的是一个方法方法体,也就是大括号和大括号内不的内容,即"{函数体...}"。
因此,我们实际使用的时候通常会将 function() 省略不写,就变成了下面这种格式
$.ajax({
函数体...
})
此外,函数体包含以下几个参数,参数与参数之间使用","逗号进行分割,每一组参数之间使用":"冒号进行分割;并且称它们的顺序没有要求,谁写在最上面都可以。如下是一种常用的书写方式,
$.ajax({
url:"",
type:"",
async:"",
data:"",
dataType:"",
contentType:"",
success:function(result){
//result:服务器响应的数据
//处理服务器响应的数据
},
error:function(){
//处理请求失败的情况
}
})
各个参数的含义如下表格所示
参数 | 功能描述 |
url | 表示请求的地址(url地址),不能包含参数列表的内容。 url举例:http://localhost:8080/users/reg |
type | 请求类型(常用请求类型为GET和POST)。 type举例 type:"POST" |
async | 是否同步刷新数据,默认为 ture 开启状态 |
data | 向指定的URL地址提交的数据。 举例如 data:"username=tom&password=123" |
dataType | 提交的数据类型,一般都是指定为JSON格式 举例如 dataType:"json" |
contentType | 设置请求头 |
success | 服务器正常响应客户端时,会自动调用success参数的方法,将服务器返回的数据以参数的形式传递给这个方法的参数上 |
error | 当服务器为正常响应客户端时,会自动调用error参数的方法,将服务器返回的数据以参数的形式传递给这个方法的参数上 |
三. Ajax 请求代码举例
3.1 确认用户名是否被占用例子
3.1.1 用户注册界面展示
我们都知道,很多系统中,用户昵称都是唯一的,特别是在游戏中,所以我们做一个用户昵称确认的方法,当用户输入玩用户名称之后,校验用户名是否已被占用,
当我们启动项目后,在用户注册界面,用户只要在文本框内输入用户名注册,再点击其它地方,就会触发失焦事件,发送HTTP请求到后端去查询用户希望注册的用户名是否已经存在,效果如下图所示;
当然了,在实际公司开发的项目中,通常不会采用弹窗的方式提示用户,而是在用户名一行下面显示红色字体进行提示;我们这里就不做那么麻烦了,主要是为了让同学们知道这种需求的思路设计。
3.1.2 前端用户注册表单代码
<form id="form-reg" class="form-horizontal" role="form">
<!--用户名-->
<div class="form-group">
<label class="col-md-3 control-label">名字:</label>
<div class="col-md-8">
<input id="username" name="username" type="text" class="form-control" placeholder="请输入用户名">
</div>
</div>
<!--密码-->
<div class="form-group">
<label class="col-md-3 control-label"> 密码:</label>
<div class="col-md-8">
<input id="password" name="password" type="text" class="form-control" placeholder="请输入密码">
</div>
</div>
<!--确认密码-->
<div class="form-group">
<label class="col-md-3 control-label"> 确认密码:</label>
<div class="col-md-8">
<input type="text" class="form-control" placeholder="请再次输入密码">
</div>
</div>
<!--提交按钮-->
<div class="form-group">
<label class="col-md-3 control-label"></label>
<div class="col-md-8">
<input id="btn-reg" class="btn btn-primary" type="button" value="立即注册" />
<span class="pull-right"><small>已经有账号?</small><a href="login.html">登录</a></span>
</div>
</div>
</form>
3.1.3 前端 JavaScript 发送 Ajax 请求的代码
// 用户输入用户名,发送ajax请求确认是否已经存在,
$("#username").blur(function () {
$.ajax({
url: "/users/getByUsername",
type: "get",
dataType: "json",
data: {
username: $("#username").val(),
}, // 正确的格式
success:function (json) {
if(json.code == 200 && json.data != null){
// 提示用户该用户名已被占用
alert("该用户名已被占用")
}else{
// 不做任何提示
alert("该用户名未被占用")
}
},
error:function (xhr) {
alert("查询用户名时产生未知的异常" + xhr.status+xhr.message);
},
});
});
3.1.4 后端代码
// Controller 层接口代码
@RequestMapping(value = "/getByUsername",method = GET)
public JsonResult<User> getByUsername(String username){
User data = userService.findByUsername(username);
return new JsonResult<>(OK ,data);
}
// Service 层业务类代码
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
// Dao 层数据访问层代码
User findByUsername(@Param("username") String username);
// Mapper 层SQL语句编写
<select id="findByUsername" resultType="com.cy.store.entity.User">
select * from t_user where username = #{username}
</select>
3.2 修改用户信息举例
3.2.1 浏览器页面效果展示
我们只关注中间用户信息的模块,用户登陆成功之后,点击进入修改信息界面,就会马上发送 Ajax 请求查询用户信息返回到前端,前端再进行展示;
当用户修改完成信息之后,点击修改按钮,会再次发送 Ajax 请求,将新的用户数据传递给后端服务器,然后修改书库结果,然后前端同步刷新用户数据,根本不需要刷新浏览器页面,非常快捷方便。
3.2.2 用户表单代码
设置表单 id="form-change-info";修改按钮 id="btn-change-info";
<!--修改资料表单开始-->
<form id="form-change-info" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-md-2 control-label">用户名:</label>
<div class="col-md-8">
<input class="form-control" id="username" name="username" type="text">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">电话号码:</label>
<div class="col-md-8">
<input class="form-control" id="phone" name="phone" type="text">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">电子邮箱:</label>
<div class="col-md-8">
<input class="form-control" id="email" name="email" type="text">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">性别:</label>
<div class="col-md-8">
<label class="radio-inline">
<input id="gender-male" type="radio" name="gender" value="1">男
</label>
<label class="radio-inline">
<input id="gender-female" type="radio" name="gender" value="0">女
</label>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input id="btn-change-info" type="button" class="btn btn-primary" value="修改" />
</div>
</div>
</form>
3.2.3 前端 JavaScript 代码
既然用户修改信息,进入用户信息页面,必然会有旧的信息。所以我们需要先定义一个定义一个初始填充数据的请求,正如下带代码所示,然后再获取需该按钮并为其绑定单击事件,单击之后发送 ajax 请求到后端修改数据
<script>
// 进入修改信息页面,发送ajax请求先查询出已有的用户信息渲染到页面
$(document).ready(function () {
$.ajax({
url: "/users/getByUid",
type: "GET",
dataType: "json",
success: function(json) {
if (json.code == 200) {
$("#username").val(json.data.username);
$("#email").val(json.data.email);
$("#phone").val(json.data.phone);
let radio = json.data.gender == 0 ? $("#gender-female"): $("#gender-male");
radio.prop("checked", true);
} else {
alert("修改失败");
}
},
error: function(xhr) {
alert("修改失败"+ xhr.message);
}
});
});
// 用户点击修改按钮,发送ajax请求修改用户信息
$("#btn-change-info").click(function (){
$.ajax({
url: "/users/changeInfo",
type: "POST",
dataType: "json",
data: $("#form-change-info").serialize(),
success: function(json) {
if (json.code == 200) {
alert("修改成功");
location.href = "userdata.html";
}else{
alert("修改失败");
}
},
error: function(xhr) {
alert("修改失败"+ xhr.message);
},
});
});
</script>
3.2.4 后端 Controller 层接口代码
上述 Ajax 请求正是由后端的接口进行接收并处理;
/* 获取用户信息 */
@RequestMapping(value = "/getByUid",method = GET)
public JsonResult<User> getUserByUid(HttpSession session){
Integer uid = getUidFromSession(session);
User data = userService.findByUid(uid);
return new JsonResult<>(OK ,data);
}
/* 用户修改个人资料 */
@RequestMapping(value = "/changeInfo", method = POST)
public JsonResult<Void> changeInfo(User user,HttpSession session){
// user对象中要四个数据,username,phone,email,gender
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
userService.changeInfo(uid,username,user);
return new JsonResult<>(OK);
}
3.2.5 业务层代码代码
@Override
public User findByUid(Integer id) {
User result = userMapper.findByUid(id);
if (result == null || result.getIsDelete() == 1){
throw new UserNotFoundException("该用户不存在");
}
User user = new User();
user.setUsername(result.getUsername());
user.setPhone(result.getPhone());
user.setEmail(result.getEmail());
user.setGender(result.getGender());
return user;
}
@Override
public void changeInfo(Integer id, String username, User user) {
User result = userMapper.findByUid(id);
if (result == null || result.getIsDelete() == 1){
throw new UserNotFoundException("该用户不存在");
}
user.setUid(id);
user.setModifiedUser(username);
user.setModifiedTime(new Date());
Integer rows = userMapper.updateInfoByUid(user);
if (rows != 1){
throw new UpdateException("更新时发生错误");
}
}
3.2.6 DAO层代码,Mapper层代码
User findByUid(@Param("uid") Integer uid);
<select id="findByUid" resultType="com.cy.store.entity.User">
select * from t_user where uid = #{uid}
</select>
Integer updateInfoByUid(User user);
<update id="updateInfoByUid">
update t_user set
<!--if表示条件判断标签,test接收的是返回值为布尔类型的条件,如果test结果为true,则执行if标签内的语句-->
<if test="phone!=null">phone = #{phone},</if>
<if test="email!=null">email = #{email},</if>
<if test="gender!=null">gender = #{gender},</if>
modified_user = #{modifiedUser},
modified_time = #{modifiedTime}
where uid = #{uid}
</update>
3.3 代码仓库及相关视频
小编这里举的代码例子也是从B站上学习的,相关视频链接
【SpringBoot项目实战完整版】SpringBoot+MyBatis+MySQL电脑商城项目实战_哔哩哔哩_bilibili【SpringBoot项目实战完整版】SpringBoot+MyBatis+MySQL电脑商城项目实战共计41条视频,包括:01-项目环境搭建、02-用户注册-持久层、03-用户注册-业务层等,UP主更多精彩视频,请关注UP账号。https://www.bilibili.com/video/BV1bf4y1V7Bx/?spm_id_from=333.788.videopod.episodes&vd_source项目的中代码我已经存储到了自己的 Gitee 仓库中,仓库已经公开,小伙伴们可以自己拉取,具体操作可以看项目MD文档,适合SSM和SpringBoot小白同学做的一个前端项目。
项目目前代码仍在持续更新过程中,小编也会把自己学习和编写的代码推到仓库中,小编主要也是为了学习 Ajax 发送请求到后端与后端交互这一知识点,所以主要推送前端代码,效果伙伴们也可以自己看视频自己敲一遍哦。
store: 电脑商城项目源码,包含前后端。https://gitee.com/haust_zhang/store
四. Ajax的优缺点
Ajax 的优点:
(1)可以无需刷新页面与服务器进行通信;
(2)允许根据用户事件来更新部分页面内容;
Ajax 的缺点:
(1)没有浏览历史,不能回退;
(2)存在跨域问题(同源),有解决办法,下文会讲到;
(3)SEO不友好;