目录
11. SpringMVC 环境下的 Ajax 请求
11.1 建立意识
前端发送过来,后端要处理的请求有两种:
普通请求:后端处理完成后返回页面,浏览器使用使用页面替换整个窗口中的内容
Ajax 请求:后端处理完成后通常返回 JSON 数据,jQuery 代码使用 JSON 数据
对页面局部更新
11.2 常用注解
ResponseBody和@RequestBody要想正常工作必须有jackson的支持,请确认当前环境引入了如下依赖
11.2.2加入依赖
<!-- Spring进行JSON数据转换依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- JSP 页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
11.2.3 加入 jQuery
11.3 @RequestBody 注解
11.3.1 作用
让 handler 方法的返回值本身就是当前请求的响应数据。不再参考视图处理器
中配置的前后缀信息。
@Controller
public class TestHandler {
@Autowired
private AdminService adminService;
private Logger logger = LoggerFactory.getLogger(TestHandler.class);
@RequestMapping("/test/ssm.html")
public String testSsm(ModelMap modelMap){
List<Admin> adminList = adminService.getAll();
modelMap.addAttribute("adminList", adminList);
return "target";
}
}
11.3.2 注意
开启 SpringMVC 的注解驱动
mvc:annotation-driven/
必须有 jackson 依赖
jackson-core
jackson-databind
扩展名需要和实际返回的数据格式一致
响应体返回 JSON
请求扩展名*.json
web.xml 中 DispatcherServlet 必须映射*.json 扩展名
11.4 @ResponseBody 注解
11.4.1 场景设定
jQuery 通过 Ajax 请求给服务器端发送一个数组:[5,8,12]
11.4.2 尝试方案一
前端发送数据
$("#btn1").click(function () {
$.ajax({
"url": "send/array/one.html", //请求目标资源的地址
"type": "post", //请求方式
"data": {
"array": [5,8,12]
}, //要发送的请求参数
"dataType": "text", //如何对待服务器端返回的数据
"success": function (response) { //服务器端成功处理请求后调用的回调函数,
alert(response);
},
"error": function (response) { //服务器端处理请求失败后调用的回调函数,
alert(response);
}
});
});
浏览器开发者工具看到的请求体
jQuery 私自在请求参数名字后面附加了“[]”
后端接收数据
@ResponseBody
@RequestMapping("/send/array/one.html")
public String testReceiveArrayOne(@RequestParam("array[]") List<Integer> array){
for (Integer number : array){
System.out.println("number=" + number);
}
return "success";
}
这里一定要带上多出来的方括号!!!
结论:不建议采用这个方案!!!
11.4.3 尝试方案二
前端发送数据
$("#btn2").click(function () {
$.ajax({
"url": "send/array/two.html", //请求目标资源的地址
"type": "post", //请求方式
"data": {
"array[0]": 5,
"array[1]": 8,
"array[2]": 12
}, //要发送的请求参数
"dataType": "text", //如何对待服务器端返回的数据
"success": function (response) { //服务器端成功处理请求后调用的回调函数,
alert(response);
},
"error": function (response) { //服务器端处理请求失败后调用的回调函数,
alert(response);
}
});
});
浏览器开发者工具看到的请求体
后端接收数据
@ResponseBody
@RequestMapping("/send/array/two.html")
public String testReceiveArrayTwo(ParamData paramData){
List<Integer> array = paramData.getArray();
for (Integer number : array){
System.out.println("number=" + number);
}
return "success";
}
结论:这个方案不成立!
11.4.4 尝试方案三
前端发送数据
$("#btn3").click(function () {
//准备好要发送到服务器端的数组
var array = [5, 8, 12];
console.log(array.length);
//将JSON数组转换未JSON字符串
var requestBody = JSON.stringify(array);
//"['5', '8', '12']"
console.log(requestBody.length);
$.ajax({
"url": "send/array/three.html", //请求目标资源的地址
"type": "post", //请求方式
"data": requestBody, //要发送的请求参数
"contentType": "application/json;charset=UTF-8", //设置请求体的内容类型,告诉服务器端本次请求的请求体是JSON数据
"dataType": "text", //如何对待服务器端返回的数据
"success": function (response) { //服务器端成功处理请求后调用的回调函数,
alert(response);
},
"error": function (response) { //服务器端处理请求失败后调用的回调函数,
alert(response);
}
});
});
浏览器开发者工具看到的请求体
后端接收数据
@ResponseBody
@RequestMapping("send/array/three.html")
public String testReceiveArrayThree(@RequestBody List<Integer> array){
for (Integer number : array) {
logger.info("number=" + number);
}
return "success";
}
结论:建议使用的方案!!!
11.4.5 需要注意的点
前端
首先准备好要发送的 JSON 数据
JSON 对象
JSON 数组
将 JSON 对象或 JSON 数组转换为 JSON 字符串
var arrayStr = JSON.stringify(array);
将 JSON 字符串直接赋值给 data 属性
“data”:arrayStr
必须要设置 contentType
“contentType”:“application/json;charset=UTF-8”
后端
加入 jackson 依赖
开启注解驱动
使用注解
@RequestBody Integer[] empIdArray
11.4.6 发送复杂对象举例
前端发送数据
$("#btn4").click(function () {
//准备要发送的数据
var student = {
"stuId": 5,
"stuName": "tom",
"address": {
"province": "广东",
"city": "深圳",
"street": "后瑞"
},
"subjectsList": [
{
"subjectName": "JavaSE",
"subjectScore": 100
},{
"subjectName": "SSM",
"subjectScore": 99
}
],
"map": {
"k1": "v1",
"k2": "v2"
}
};
//将JSON对象转换未JSON字符串
var requestBody = JSON.stringify(student);
//发送Ajax请求
$.ajax({
"url": "/send/compose/object.json",
"type": "post",
"data": requestBody,
"contentType": "application/json;charset=UTF-8",
"dataType": "json",
"success": function (response) {
console.log(response);
},
"error": function (response) {
console.log(response);
}
})
});
浏览器开发者工具中看到的请求体
后端接收数据
@ResponseBody
@RequestMapping("/send/compose/object.json")
public ResultEntity<Student> testReceiveComposeObject(@RequestBody Student student, HttpServletRequest request){
boolean judgeResult = CrowdUtil.judgeRequestType(request);
logger.info("judgeResult=" + judgeResult);
logger.info(student.toString());
//将"查询"到的Student对象封装到ResultEntity中返回
ResultEntity<Student> resultEntity = ResultEntity.successWithData(student);
String a = null;
System.out.println(a.length());
return resultEntity;
}
11.4.7小结
@RequestBody 使用的场景:传统发送请求参数方式不方便发送的数据,使用
JSON 请求体的方式发送。特别是要发送复杂对象的时候。
11. 5对比
11.6 统一返回数据格式
/**
* 用于统一项目中所有 Ajax 请求的返回值类型
* @author Lenovo
*
* @param <T>
*/
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
public static final String NO_MESSAGE = "NO_MESSAGE";
public static final String NO_DATA = "NO_DATA";
/**
* 返回操作结果为成功,不带数据
* @return
*/
public static <E> ResultEntity<E> successWithoutData() {
return new ResultEntity<E>(SUCCESS, NO_MESSAGE, null);
}
/**
* 返回操作结果为成功,携带数据
* @param data
* @return
*/
public static <E> ResultEntity<E> successWithData(E data) {
return new ResultEntity<E>(SUCCESS, NO_MESSAGE, data);
}
/**
* 返回操作结果为失败,不带数据
* @param message
* @return
*/
public static <E> ResultEntity<E> failed(String message) {
return new ResultEntity<E>(FAILED, message, null);
}
private String operationResult;
private String operationMessage;
private T queryData;
public ResultEntity() {
}
public ResultEntity(String operationResult, String operationMessage, T queryData) {
super();
this.operationResult = operationResult;
this.operationMessage = operationMessage;
this.queryData = queryData;
}
@Override
public String toString() {
return "AjaxResultEntity [operationResult=" + operationResult + ", operationMessage="
+ operationMessage
+ ", queryData=" + queryData + "]";
}
public String getOperationResult() {
return operationResult;
}
public void setOperationResult(String operationResult) {
this.operationResult = operationResult;
}
public String getOperationMessage() {
return operationMessage;
}
public void setOperationMessage(String operationMessage) {
this.operationMessage = operationMessage;
}
public T getQueryData() {
return queryData;
}
public void setQueryData(T queryData) {
this.queryData = queryData;
}
}
12. 前端页面
12.1 静态资源引入
12.1.1 来源
“前端页面”目录下
12.1.2 复制到工程中的位置
12.2 初步显示管理员登录页面
12.2.1 准备页面文件
12.2.2 调整admin-login.jsp页面文件
把页面文件的内容从原型文件中复制过来,再进行必要的调整
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<style>
</style>
</head>
12.2.3 配置 view-controller
12.3layer 弹层组件
12.3.1 解压
12.3.2 加入 layer 环境
12.3.3 页面引入 layer
12.3.4 使用
13 异常映射
13.1 作用
统一管理项目中的异常
抛出异常
显示异常信息
普通请求:在页面上显示异常信息
Ajax 请求:返回 JSON 数据
13.2 异常映射的工作机制
13.3 判断请求类型的工具方法
13.3.1 判断依据
13.3.2 创建工具类
13.3.3 代码
/**
* 判断当前请求是否为Ajax请求
*
* true:当前请求是Ajax请求
* false:当前请求不是Ajax请求
*/
public static boolean judgeRequestType(HttpServletRequest request){
//1.获取请求消息头
String acceptHeader = request.getHeader("Accept");
String xRequestHeader = request.getHeader("X-Requested-With");
//2.判断
if ((acceptHeader != null && acceptHeader.contains("application/json"))
||
(xRequestHeader != null && xRequestHeader.equals("XMLHttpRequest"))){
return true;
}
return false;
}
13.4 异常映射实现方式
13.4.1 基于 XML
spring-web-mvc.xml 文件中的配置
<!--配置基于XML的异常映射-->
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 配置异常类型和具体视图页面的对应关系-->
<property name="exceptionMappings">
<props>
<!-- key属性指定异常全类名-->
<!-- 标签中写对应的视图(这个值要拼前后缀得到具体路径)-->
<prop key="java.lang.Exception">system-error</prop>
<prop key="com.atguigu.crowd.exception.AccessForbiddenException">admin-login</prop>
</props>
</property>
</bean>
新建 system-error.jsp 页面
13.4.2 基于注解
创建异常处理器类
@ExceptionHandler(value = ArithmeticException.class)
public ModelAndView resolveMathException(
ArithmeticException exception,
//当前请求对象
HttpServletRequest request,
//当前响应对象
HttpServletResponse response
) throws IOException {
String viewName = "admin-login";
return commonResolve(viewName, exception, request, response);
}
// @ExceptionHandler将一个具体的异常类型和一个方法关联起来
@ExceptionHandler(value = NullPointerException.class)
public ModelAndView resolveNullPointerException(
//实际捕获到的异常类型
NullPointerException exception,
//当前请求对象
HttpServletRequest request,
//当前响应对象
HttpServletResponse response) throws IOException {
String viewName = "system.error";
return commonResolve(viewName, exception, request, response);
}
private ModelAndView commonResolve(
//异常处理完成后要去的页面
String viewName,
//实际捕获到的异常类型
Exception exception,
//当前请求对象
HttpServletRequest request,
//当前响应对象
HttpServletResponse response) throws IOException {
//1.判断当前请求类型
boolean judgeReqsult = CrowdUtil.judgeRequestType(request);
//2.如果是Ajax请求
if (judgeReqsult){
//3.创建ResultEntity对象
ResultEntity<Object> resultEntity = ResultEntity.failed(exception.getMessage());
//4.创建Gson对象
Gson gson = new Gson();
//5.将ResultEntity对象转换为JSON字符串
String json = gson.toJson(resultEntity);
//6.将JSON字符串作为响应体返回给浏览器
response.getWriter().write(json);
//7.由于上面已经通过原生的 response对象返回了响应,所有不提供ModelAndView对象
return null;
}
//8.如果不是Ajax请求则创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//9.将Exception对象存入模型
modelAndView.addObject(CrowdConstant.ATTR_NAME_EXCEPTION, exception);
//10.设置对应的视图名称
modelAndView.setViewName(viewName);
//11.返回ModelAndView
return modelAndView;
}
14. 以常量管理属性名和异常消息
public static final String MESSAGE_LOGIN_FAILED = "抱歉! 账号密码错误!请重新输入!";
public static final String ATTR_NAME_EXCEPTION = "exception";
好处:减少打字错误!!!
14.1 创建管理员登录页面
15. 创建后台管理员登录页面(后台首页)
15.1 创建admin-login.jsp
15.2 跳转到登录页面
15.2.1 配置 view-controller
16. 使用layer弹层组件
16.1 加入layer库文件和样式文件
16.2 在页面上引入layer环境
16.3 修饰system-error页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function () {
$("button").click(function () {
//相当于浏览器的后退按钮
window.history.back();
});
});
</script>
<style>
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<div><a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a></div>
</div>
</div>
</nav>
<div class="container">
<h2 class="form-signin-heading" style="text-align: center;">
<i class="glyphicon glyphicon-log-in"></i> 尚筹网系统消息
</h2>
<%--
requestScope对应的是存放request域数据的Map
requestScope.exception相当于request.getAttribute("exception")
requestScope.exception.message相当于exception.getMessage()
--%>
<h3 style="text-align: center;">${requestScope.exception.message}</h3>
<button type="submit" style="width: 150px; margin: 50px auto 0px auto" class="btn btn-lg btn-success btn-block">
点我返回上一步</button>
</div>
</body>
</html>