Ajax:Asynchronous Javascript And XML(异步 JavaScript 和 XML)
使用 Ajax 技术,网页能够快速地将增量更新呈现在用户界面上,而不需要刷新整个页面,这使得程序能够更快地回应用户的操作;
Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术;
使用 Ajax 的最大优点,就是能在不更新整个页面的前提下维护数据,即在不需要刷新页面的情况下,就可以产生局部刷新的效果。
需要用到 jQuery 实现 Ajax,它是一个快速、简洁的 JavaScript 框架,具有 js 的大量函数,引入 jQuery 的方法:
[Download the uncompressed, development jQuery 3.7.0]
;Ctrl + S
保存到下载的位置;使用格式
<%-- 引入 jQuery 框架 --%>
<script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>
<script>
function xxx() {
$.post({
url:"${pageContext.request.contextPath}/请求地址",
data:{"后端参数名":$("#前端参数名").val()},
success:function (data) {
成功之后执行的回调函数;
},
error:function (data) {
失败之后执行的回调函数;
}
})
}
</script>
注意:
- script 标签必须成对,并且放在 body 的最后;
$.post()
相当于jQuery.post()
,使用时一定要提前引入 jQuery 框架;$.post
要放在函数内,其中的参数有:
- url :请求地址;
- data :前端要发送给后端的数据,没有数据时可以省略;
- success :成功之后执行的回调函数,注意这里回调函数中参数 data 与上面的 data 不是同一个,这里是指后端传递给前端的数据;
- error :失败之后执行的回调函数。
- 之前是由后端控制转发或重定向,完成视图的跳转,而现在是由前端 Ajax 来控制,后端只需要提供数据(JSON 字符串)即可,Ajax 把主动权交给前端。
举例:当用户输入一个信息后,进行弹窗提示。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试</title>
<script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>
<script>
function a() {
$.post({
url:"${pageContext.request.contextPath}/mon/a1",
data:{"name":$("#username").val()},
success:function (data) {
alert(data);
}
})
}
</script>
</head>
<body>
<h1>
<%-- 失去焦点时,发起一个请求(携带信息)到后台 --%>
用户名:<input type="text" id="username" οnblur="a()">
</h1>
</body>
</html>
注意:
- 前端向后端传递单个数据时,data 必须是键值对(JSON 字符串)的形式,这里的
name
为后端的参数名,username
为前端的参数名;- 在 Ajax 中,前端参数的值(例如用户输入的信息)可以通过
$("#前端参数名").val()
的方式取到;console.log
可以在控制台输出信息;alert
代表弹窗;onblur
代表失去焦点时,执行函数;- 该代码可以执行的操作是:在前端页面中,用户输入用户名信息,失去焦点时,执行函数
a()
,发起/mon/a1
请求并携带前端数据给后端,后端执行成功(success)后,执行回调函数,此时回调函数的参数 data 为后端返回的数据,进行 alert 弹窗。
后端控制层代码
// 控制层中所有返回的字符串都不会经过视图解析器,而是直接返回字符串
@RestController
@RequestMapping("/mon")
public class AjaxController {
@RequestMapping("/a1")
public String a1(String name) {
System.out.println(name);
if ("Sun3285".equals(name)) {
return "true";
} else {
return "false";
}
}
}
执行
举例:当用户点击【加载数据】按钮时,加载后台中的数据。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试</title>
<script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>
<script>
$(function () {
$("#btn").click(
function () {
$.post({
url:"${pageContext.request.contextPath}/mon/a2",
success:function(data) {
console.log(data);
var obj = JSON.parse(data);
console.log(obj);
var html = "";
for (let i = 0; i < obj.length; i++) {
html += "<tr>" +
"<td>" + obj[i].id + "</td>" +
"<td>" + obj[i].name + "</td>" +
"<td>" + obj[i].sex + "</td>" +
+ "</tr>";
}
$("#content").html(html);
}
})
}
)
})
</script>
</head>
<body>
<input type="button" value="加载数据" id="btn">
<table>
<tr>
<td>编号</td>
<td>姓名</td>
<td>性别</td>
</tr>
<tbody id="content">
<%-- 后台数据 --%>
</tbody>
</table>
</body>
</html>
注意:
- 重点看 Ajax 部分,因为前端不给后端传递数据,因此省略了 data;
- 后端传递给前端的数据 data 为 JSON 字符串,因此要先通过
JSON.parse(data)
解析为 JavaScript 对象,可以通过 console.log 来看到解析前后的结果;- 该代码可以执行的操作是:在前端页面中,用户点击【加载数据】按钮时,通过
$("#btn").click
可以捕捉到,然后执行函数,发起/mon/a2
请求给后端,后端执行成功(success)后,执行回调函数,此时回调函数的参数 data 为后端返回的数据,即一个 JSON 字符串,经过前端处理后,在页面中显示信息。
后端控制层代码
@RestController
@RequestMapping("/mon")
public class AjaxController {
@RequestMapping("/a2")
public String a2() throws JsonProcessingException {
ArrayList<Student> studentList = new ArrayList<Student>();
Student s1 = new Student(1, "Sun1234", "男");
Student s2 = new Student(2, "Sun3285", "男");
Student s3 = new Student(3, "Sun4399", "女");
Collections.addAll(studentList, s1, s2, s3);
// 后端传递给前端的是一个 JSON 字符串
return new ObjectMapper().writeValueAsString(studentList);
}
}
执行
需求:输入用户名和密码时,动态显示输入信息是否正确。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
<script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>
<script>
function login1() {
$.post({
url:"${pageContext.request.contextPath}/mon/login1",
data:{"username":$("#username").val()},
success:function (data) {
console.log(data);
if (data === "OK") {
$("#usernameInfo").css("color", "green");
} else {
$("#usernameInfo").css("color", "red");
}
$("#usernameInfo").html(data);
}
})
}
function login2() {
$.post({
url:"${pageContext.request.contextPath}/mon/login2",
data:{"username":$("#username").val(), "password":$("#password").val()},
success:function (data) {
console.log(data);
if (data === "OK") {
$("#passwordInfo").css("color", "green");
} else {
$("#passwordInfo").css("color", "red");
}
$("#passwordInfo").html(data);
}
})
}
</script>
</head>
<body>
<p>
用户名:<input type="text" id="username" οnblur="login1()"><span id="usernameInfo"></span>
<br>
密 码:<input type="text" id="password" οnblur="login2()"><span id="passwordInfo"></span>
</p>
</body>
</html>
后端控制层代码
@RestController
@RequestMapping("/mon")
public class AjaxController {
@RequestMapping("/login1")
public String login1(String username) {
if ("Sun3285".equals(username)) {
return "OK";
} else {
return "用户名输入有误";
}
}
@RequestMapping("/login2")
public String login2(String username, String password) {
String result = login1(username);
if ("OK".equals(result)) {
if ("123456".equals(password)) {
return "OK";
} else {
return "密码输入有误";
}
} else {
return result;
}
}
}
注意:判断密码是否正确的前提是:先要判断用户名是否正确。
执行
拦截器是 AOP 思想的具体应用,拦截器拦截的是请求,不拦截静态资源。
功能:用于对 SpringMVC 处理器进行预处理和后处理,可以自己定义一些拦截器来实现特定的功能。
过滤器和拦截器的区别:
过滤器:
/*
之后,可以对所有要访问的资源进行过滤;web.xml
中进行配置。拦截器:
实现拦截器的步骤:
// 第一步:自定义一个拦截器 MyInterceptor,实现 HandlerInterceptor 接口,重写方法
public class MyInterceptor implements HandlerInterceptor {
// 处理前
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
// 处理后
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
// 清理
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
注意:preHandle 方法中,返回值为 true,表示执行下一个拦截器(放行),返回值为 false,表示不执行下一个拦截器(拦截)。
<!-- 第二步:在 Spring 配置文件中,对拦截器进行配置 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- /** 表示所有请求都会经过拦截器 -->
<mvc:mapping path="/**"/>
<!-- 指定使用的拦截器 -->
<bean class="com.Sun3285.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
需求:登录判断验证,如果进行了登录操作,并且用户名和密码正确,才可以进入首页,否则不能直接进入首页。
拦截前:可以进入登录界面进行登录操作,输入正确的用户名和密码,进入主界面,也可以在首页上直接点击超链接,进入主界面。
点击【主页面】时使用拦截器拦截,拦截的依据是:是否在 Session 中存放了指定的信息,因为只有登录成功,才会在 Session 中存放指定信息 usernameID。
第一步:自定义拦截器,如果 Session 中存放了指定的信息(进行了登录操作),就放行,否则,跳转到登录页面。
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if (session.getAttribute("usernameID") != null) {
return true;
}
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
第二步:在 Spring 配置文件中,配置拦截器,在首页点击【主页面】超链接时,进行拦截。
<!-- 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/toMain"/>
<bean class="com.Sun3285.config.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
运行结果
如果想使用 Spring 的文件上传功能,需要在上下文中配置 MultipartResolver。
为了能够上传文件,前端表单要求:
后端要求:
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
文件上传的步骤:
@Controller
public class FileController {
// @RequestParam("file") 将 name="file" 控件得到的文件封装为 CommonsMultipartFile 对象
@RequestMapping("/upload")
public String uplode(@RequestParam("file") CommonsMultipartFile file,
HttpServletRequest request) throws IOException {
// 获取文件名
String uploadFilename = file.getOriginalFilename();
// 上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
// realPath.mkdirs();
}
// 输出上传文件地址
System.out.println("上传文件保存地址:" + realPath);
// 通过 CommonsMultipartFile 的方法直接写文件
file.transferTo(new File(realPath + "/" + uploadFilename));
return "redirect:/index.jsp";
}
}
注意:这部分代码可以直接使用,只需要按需求改一下上传路径 path 即可,其中
upload
为新创建的文件夹,存放上传的文件。这里也可以把上传路径 path 直接用一个字符串表示。
<!-- 在 Spring 配置文件中配置文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,可以省略,单位为字节(1MB=1048576字节) -->
<property name="maxUploadSize" value="10485760"/>
</bean>
文件上传后,控制台输出上传文件的地址,并且可以找到已上传的文件
固定代码
@Controller
public class FileController {
@RequestMapping("/download")
public String download(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 要下载的文件地址和文件名
String path = request.getServletContext().getRealPath("/files");
String fileName = "导入的依赖总结.txt";
File file = new File(path, fileName);
// 设置 response 响应头
response.reset(); // 设置页面不缓存,清空 buffer
response.setCharacterEncoding("utf-8"); // 字符编码
response.setContentType("multipart/form-data"); // 二进制传输数据
// 设置下载文件
response.setHeader("Content-Disposition",
"attachment;fileName=" + URLEncoder.encode(fileName, "utf-8"));
// 读取文件:输入流
InputStream is = new FileInputStream(file);
InputStream bis = new BufferedInputStream(is);
// 写出文件:输出流
OutputStream os = response.getOutputStream();
OutputStream bos = new BufferedOutputStream(os);
// 下载操作(读取、写出)
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bis.read(buffer)) != 0) {
bos.write(buffer, 0, len);
bos.flush();
}
// 关闭资源
bos.close();
bis.close();
return null;
}
}
注意:
- 这部分代码可以直接使用,只需要按需求改一下要下载的文件地址和文件名,其中:
- 被下载文件的地址:这里是放在了 web 文件夹下的 files 文件夹中,最后会在生成的 out 文件夹下的子项目中;
- 文件名:被下载的文件名。
- 记得最后要关闭资源,后开先关。
无法下载文件,但后端代码执行成功
判断应该是浏览器阻止了下载,更换浏览器再次尝试,可以下载成功
request.getServletContext().getRealPath("/upload")
来将文件上传到服务器生成的 out 目录下的子项目中的 upload 包(如果没有 upload 包,会自动新建)中;request.getServletContext().getRealPath("/files")
得到。Remove Module
移除模块,然后再右键删除文件夹,注意:要在父模块的 pom.xml
中删除对应的子模块。