目录
一、同步交互和异步交互
二、Ajax
1.概述
2.如何实现ajax请求
三、异步传输数据乱码的问题
regist.html页面代码
服务端代码处理
四、Axios
1. Axios的基本使用
(1)引入Axios文件
(2)使用Axios发送请求,并获取响应结果。
2.案例
3.请求方法的别名(推荐用这个,简单)
一、同步交互和异步交互
同步交互:在同步交互中,参与的各方在同一时间进行交流。请求一项操作后,发起方必须等待响应才能继续进行下一步操作
异步交互:在异步交互中,参与的各方可以在不同的时间进行交流。发起方发送请求后,可以继续进行其他操作,而不必等待响应。
二、Ajax
1.概述
Ajax是如何实现异步交互的?
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。
XMLHttpRequest 只是实现 Ajax 的一种方式。
简单来说,我们之前发的请求通过类似 form表单标签,a标签 这种方式,现在通过 运行js代码动态决定什么时候发送什么样的请求
通过运行JS代码发送的请求浏览器可以不用跳转页面 ,我们可以在JS代码中决定是否要跳转页面
通过运行JS代码发送的请求,接收到返回结果后,我们可以将结果通过dom编程渲染到页面的某些元素上,实现局部更新
2.如何实现ajax请求
原生javascript方式进行ajax(了解):
<script> function loadXMLDoc(){ // 创建XMLHttpRequest对象 var request=new XMLHttpRequest(); // 设置回调函数处理响应结果 // request.readyState 1 2 3 4(只需要知道4表示收到服务器的响应) // request.status 响应状态码响应行状态码 request.onreadystatechange=function(){ if (request.readyState==4 && request.status==200) { document.getElementById("myDiv").innerHTML=request.responseText; } } // 设置请求方式和请求的资源路径 request.open("GET","/try/ajax/ajax_info.txt",true); // 发送请求 request.send(); } </script>
三、异步传输数据乱码的问题
由于Ajax由原生js接收响应数据解码的问题,可能会出现乱码的现象,一般标准的规定是将传输的数据用JSON数据串传输。
比如下面的场景,一个注册页面,姓名的验证问题
前端页面要将后端响应的数据(JSON串)转发为JSON对象
后端要将数据存到一个对象,将对象转发为JSON串响应回去
regist.html页面代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .ht{ text-align: center; color: cadetblue; font-family: 幼圆; } .tab{ width: 500px; border: 5px solid cadetblue; margin: 0px auto; border-radius: 5px; font-family: 幼圆; } .ltr td{ border: 1px solid powderblue; } .ipt{ border: 0px; width: 50%; } .btn1{ border: 2px solid powderblue; border-radius: 4px; width:60px; background-color: antiquewhite; } .msg { color: gold; } .buttonContainer{ text-align: center; } </style> <script> // 校验用户名的方法 function checkUsername(){ // 定义正则 var usernameReg=/^[a-zA-Z0-9]{5,10}$/ var username =document.getElementById("usernameInput").value var usernameMsgSpan =document.getElementById("usernameMsg") if(!usernameReg.test(username)){ usernameMsgSpan.innerText="不合法" return false } // 发送ajax请求校验用户名是否被占用 var request; if(window.XMLHttpRequest){ request= new XMLHttpRequest(); }else{ request= new ActiveXObject("Microsoft.XMLHTTP"); } request.onreadystatechange= function (){ // request.readyState == 4 代表请求结束,已经接收到响应结果 // request.status== 200 表示后端响应状态码是200 if(request.readyState == 4 && request.status== 200){ // 后端的响应的JSON字符串转换为前端的对象 var response =JSON.parse(request.responseText) console.log(response) // 判断业务码是否是200 if (response.code != 200){ usernameMsgSpan.innerText="已占用" return false } } } // 设置请求方式,请求资源路径,是否为异步请求 request.open("GET",'/user/checkUsernameUsed?username='+username,true) // 发送请求 request.send(); // 前面校验都通过 // usernameMsgSpan.innerText="OK" // return true } // 校验密码的方法 function checkUserPwd(){ // 定义正则 var passwordReg=/^[0-9]{6}$/ var userPwd =document.getElementById("userPwdInput").value var userPwdMsgSpan =document.getElementById("userPwdMsg") if(!passwordReg.test(userPwd)){ userPwdMsgSpan.innerText="不合法" return false } userPwdMsgSpan.innerText="OK" return true } // 校验密码的方法 function checkReUserPwd(){ // 定义正则 var passwordReg=/^[0-9]{6}$/ var userPwd =document.getElementById("userPwdInput").value var reUserPwd =document.getElementById("reUserPwdInput").value var reUserPwdMsgSpan =document.getElementById("reUserPwdMsg") if(!passwordReg.test(userPwd)){ reUserPwdMsgSpan.innerText="不合法" return false } if(userPwd != reUserPwd){ reUserPwdMsgSpan.innerText="不一致" return false } reUserPwdMsgSpan.innerText="OK" return true } //表单提交时统一校验 function checkForm(){ return checkUsername() && checkUserPwd() && checkReUserPwd() } </script> </head> <body> <h1 class="ht">欢迎使用日程管理系统</h1> <h3 class="ht">请注册</h3> <form method="post" action="/user/regist" onsubmit="return checkForm()"> <table class="tab" cellspacing="0px"> <tr class="ltr"> <td>请输入账号</td> <td> <input class="ipt" id="usernameInput" type="text" name="username" onblur="checkUsername()"> <span id="usernameMsg" class="msg"></span> </td> </tr> <tr class="ltr"> <td>请输入密码</td> <td> <input class="ipt" id="userPwdInput" type="password" name="userPwd" onblur="checkUserPwd()"> <span id="userPwdMsg" class="msg"></span> </td> </tr> <tr class="ltr"> <td>确认密码</td> <td> <input class="ipt" id="reUserPwdInput" type="password" onblur="checkReUserPwd()"> <span id="reUserPwdMsg" class="msg"></span> </td> </tr> <tr class="ltr"> <td colspan="2" class="buttonContainer"> <input class="btn1" type="submit" value="注册"> <input class="btn1" type="reset" value="重置"> <button class="btn1"><a href="/login.html">去登录</a></button> </td> </tr> </table> </form> </body> </html>
服务端代码处理
添加公共的JSON数据响应格式类
package com.atguigu.schedule.common; /** * 业务含义和状态码对应关系的枚举 * */ public enum ResultCodeEnum { SUCCESS(200,"success"), USERNAME_ERROR(501,"usernameError"), PASSWORD_ERROR(503,"passwordError"), NOTLOGIN(504,"notLogin"), USERNAME_USED(505,"userNameUsed") ; private Integer code; private String message; private ResultCodeEnum(Integer code, String message) { this.code = code; this.message = message; } public Integer getCode() { return code; } public String getMessage() { return message; } }
package com.atguigu.schedule.common; /** * 全局统一响应的JSON格式处理类 * */ public class Result<T> { // 返回码 private Integer code; // 返回消息 private String message; // 返回数据 private T data; public Result(){} // 返回数据 protected static <T> Result<T> build(T data) { Result<T> result = new Result<T>(); if (data != null) result.setData(data); return result; } public static <T> Result<T> build(T body, Integer code, String message) { Result<T> result = build(body); result.setCode(code); result.setMessage(message); return result; } public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) { Result<T> result = build(body); result.setCode(resultCodeEnum.getCode()); result.setMessage(resultCodeEnum.getMessage()); return result; } /** * 操作成功 * @param data baseCategory1List * @param <T> * @return */ public static<T> Result<T> ok(T data){ Result<T> result = build(data); return build(data, ResultCodeEnum.SUCCESS); } public Result<T> message(String msg){ this.setMessage(msg); return this; } public Result<T> code(Integer code){ this.setCode(code); return this; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
增加Jackson依赖
添加WEBUtil工具类 (后面SpringMVC框架会提供方法)
package com.atguigu.schedule.util; import com.atguigu.schedule.common.Result; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.text.SimpleDateFormat; public class WebUtil { private static ObjectMapper objectMapper; // 初始化objectMapper static{ objectMapper=new ObjectMapper(); // 设置JSON和Object转换时的时间日期格式 objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); } // 从请求中获取JSON串并转换为Object public static <T> T readJson(HttpServletRequest request,Class<T> clazz){ T t =null; BufferedReader reader = null; try { reader = request.getReader(); StringBuffer buffer =new StringBuffer(); String line =null; while((line = reader.readLine())!= null){ buffer.append(line); } t= objectMapper.readValue(buffer.toString(),clazz); } catch (IOException e) { throw new RuntimeException(e); } return t; } // 将Result对象转换成JSON串并放入响应对象 public static void writeJson(HttpServletResponse response, Result result){ response.setContentType("application/json;charset=UTF-8"); try { String json = objectMapper.writeValueAsString(result); response.getWriter().write(json); } catch (IOException e) { throw new RuntimeException(e); } } }
用户名校验业务接口代码
/** * SysUserController下,注册时校验用户名是否被占用的业务接口 * @param req * @param resp * @throws ServletException * @throws IOException */ protected void checkUsernameUsed(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); SysUser registUser = userService.findByUsername(username); //封装结果对象 Result result=null; if(null ==registUser){ // 未占用,创建一个code为200的对象 result= Result.ok(null); }else{ // 占用, 创建一个结果为505的对象 result= Result.build(null, ResultCodeEnum.USERNAME_USED); } // 将result对象转换成JSON并响应给客户端 WebUtil.writeJson(resp,result); }
四、Axios
上述原生的Ajax请求的代码编写起来还是比较繁琐的,所以接下来我们学习一门更加简单的发送Ajax请求的技术Axios 。Axios是对原生的AJAX进行封装,简化书写。Axios官网是:
https://www.axios-http.cn
1. Axios的基本使用
(1)引入Axios文件
就是一个js文件,可以到官网下载。
<script src="js/axios-0.18.0.js"></script>
(2)使用Axios发送请求,并获取响应结果。
官方提供的api很多,此处给出2种,如下
发送 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); });
axios()是用来发送异步请求的,小括号中使用 js的JSON对象传递请求相关的参数:
method属性:用来设置请求方式的。取值为 get 或者 post。
url属性:用来书写请求的资源路径。如果是 get 请求,需要将请求参数拼接到路径的后面,格式为: url?参数名=参数值&参数名2=参数值2。
data属性:作为请求体被发送的数据。也就是说如果是 post 请求的话,数据需要作为 data 属性的值。
then() 需要传递一个匿名函数。我们将 then()中传递的匿名函数称为 回调函数,意思是该匿名函数在发送请求时不会被调用,而是在成功响应后调用的函数。而该回调函数中的 resp 参数是对响应的数据进行封装的对象,通过 resp.data 可以获取到响应的数据。
2.案例
向后端请求数据:
后端实现
查询所有员工信息服务器地址:http://yapi.smart-xwork.cn/mock/169327/emp/list
根据员工id删除员工信息服务器地址:http://yapi.smart-xwork.cn/mock/169327/emp/deleteById
前端实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Ajax-Axios</title> <script src="js/axios-0.18.0.js"></script> </head> <body> <input type="button" value="获取数据GET" onclick="get()"> <input type="button" value="删除数据POST" onclick="post()"> </body> <script> function get(){ //通过axios发送异步请求-get axios({ method: "get", url: "http://yapi.smart-xwork.cn/mock/169327/emp/list" }).then(result => { console.log(result.data); }) } function post(){ // 通过axios发送异步请求-post axios({ method: "post", url: "http://yapi.smart-xwork.cn/mock/169327/emp/deleteById", data: "id=1" }).then(result => { console.log(result.data); }) } </script> </html>
浏览器F12获取,然后分别点击2个按钮,查看控制台效果如下:
3.请求方法的别名(推荐用这个,简单)
Axios还针对不同的请求,提供了别名方式的api,具体如下:
方法 描述 axios.get(url [, config]) 发送get请求 axios.delete(url [, config]) 发送delete请求 axios.post(url [, data[, config]]) 发送post请求 axios.put(url [, data[, config]]) 发送put请求
在上述的案例中,我们可以将get请求代码改写成如下:
axios.get("http://yapi.smart-xwork.cn/mock/169327/emp/list").then(result => { console.log(result.data); })
post请求改写成如下:
axios.post("http://yapi.smart-xwork.cn/mock/169327/emp/deleteById","id=1").then(result => { console.log(result.data); })