一、实验一(发送简单请求参数)

1、导入 jar 包

在进行 Ajax 操作时,SpringMVC 会需要将 JSON 数据和 Java 实体类进行相互转换,为了实现这个效果,在 SpringMVC 基础 jar 包组合的基础上,再导入

jackson-all-1.9.11.jar

因为 SpringMVC 需要使用 jackson-all-1.9.11.jar 实现 JSON 字符串和 Java 对象的互转。

2、配置 xml

web.xml 还是常规配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- 加入前端控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 解决idea中文乱码问题 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

加入 characterEncodingFilter 和没有加入 characterEncodingFilter ,IDEA 输出效果:

配置 spring-mvc.xml 配置文件,配置自动扫描的包,同时打开注解驱动

1
2
3
4
<context:component-scan base-package="com.atguigu.ajax.controller"/>
<!-- Ajax相关操作中用到的@RequestBody、@ResponseBody注解必须在开启了注解驱动功能的前提下才有效 -->
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>

关于<mvc:default-servlet-handler/>

通过 web.xml 中对 url-pattern 的配置,所有 URL 请求都将被 Spring MVC 的 DispatcherServlet 截获

在 spring-mvc.xml 中配置<mvc:default-servlet-handler />后,会在 Spring MVC 上下文中定义一个 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入 DispatcherServlet 的 URL 进行筛查。如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。

一般 Web 应用服务器默认的 Servlet 名称是”default”,因此 DefaultServletHttpRequestHandler 可以找到它。如果你所有的 Web 应用服务器的默认 Servlet 名称不是”default”,则需要通过 default-servlet-name 属性显示指定:
<mvc:default-servlet-handler default-servlet-name=”所使用的 Web 服务器默认使用的 Servlet 名称” />

3、配置 index.jsp

① 导入 jquery

jquery-1.7.2

放到 web/script/目录下

②index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Ajax</title>
<script
type="text/javascript"
src="${pageContext.request.contextPath}/script/jquery-1.7.2.js"
></script>
<script type="text/javascript">
$(function () {

// 实验1:浏览器端发送简单请求参数;服务器端返回普通文本
$("#btn01").click(function () {

//发送ajax请求jquery提供有$.post,$.get,但是我们为了在服务器端处理请求失败以后也能得到相应的提示,所以我们用$.ajax方法
$.ajax({
"url":"${pageContext.request.contextPath}/ajax/exer/one", // 请求的目标地址
"type":"post", // 请求方式
"dataType":"text", // 服务器端返回数据的解析方式
"contentType":"application/x-www-form-urlencoded", // 当前发送请求参数的方式本质上就是提交一个普通的表单,所以内容类型采用默认值:application/x-www-form-urlencoded,写不写都可以
"data":{ // 要发送给服务器端的数据
"userName":"约翰",
"userPwd":"123456"
},
"success":function (response) { // 服务器端处理请求成功后调用的回调函数
console.log(response);
},
"error":function (response) { // 服务器端处理请求失败后调用的回调函数
console.log(response);
}
});
});
</script>
</head>
<body>
<button id="btn01">实验1</button>
<button id="btn02">实验2</button>
<button id="btn03">实验3</button>
</body>
</html>

③ 创建 AjaxController 类

在 com.atguigu.ajax.controller 包下创建,写出对应的后端程序,实现服务器端给响应体设置内容类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 把@ResponseBody注解加到类上,就等于给每个方法都加了这个注解
// @ResponseBody注解表示:将handler方法的返回值当作响应体返回给浏览器
// @ResponseBody
// @Controller

// @RestController注解是由@Controller和@ResponseBody组成,所以加上@RestController就等于同时加了前面两个注解
@RestController
public class AjaxController {

// 当使用@ResponseBody注解返回的响应体数据中包含中文字符时,需要在@RequestMapping注解中通过produces属性设置响应体的内容类型
// 使用@ResponseBody注解可以解决浏览器端console.log中文乱码问题
// 普通文本的内容类型:text/html
// JSON文本的内容类型:application/json
@RequestMapping(value = "/ajax/exer/one", produces = "text/html;charset=UTF-8")
public String ajaxExerOne(
@RequestParam("userName") String userName,
@RequestParam("userPwd") String userPwd) {

System.out.println("userName = " + userName);
System.out.println("userPwd = " + userPwd);

return "服务器端返回数据啦!";
}
}

没有加入 produces 属性加入 produces 属性,浏览器端 console.log 输出结果:

最后查看浏览器端发送的数据:

Form Data 就表示它是一个普通的表单数据。

二、实验二(服务器端返回 JSON 数据)

1
2
3
4
5
6
7
8
// 实验2:服务器端返回JSON数据 $("#btn02").click(function () { $.ajax({
"url":"${pageContext.request.contextPath}/ajax/exer/two.json", "type":"post",
"dataType":"json", //
这里设置为json后,jQuery会自动把JSON字符串转换为JSON对象,所以下面的方法中可以直接访问属性名
"success":function (response) { console.log(response);
console.log(response.soldierId); console.log(response.soldierName);
console.log(response.soldierSalary); }, "error":function (response) {
console.log(response); } }); });

在 AjaxController 类下增加方法

1
2
3
4
5
6
7
8
9
10
11

// 如果标记了@ResponseBody注解的handler方法返回的是复杂类型的数据
// 那么SpringMVC会自动帮我们转换成JSON数据返回,此时需要jackson的jar包支持
// @ResponseBody
@RequestMapping(value = "/ajax/exer/two", produces = "application/json;charset=UTF-8")
public Soldier ajaxExerTwo() {

Soldier soldier = new Soldier(20, "杰克", 6000.00);

return soldier;
}

三、实验三(浏览器端给服务器端发送 JSON 请求体)

测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$("#btn03").click(function () { // 1.创建JSON对象 var student = { "stuId":556,
"stuName":"carl", "school":{ "schoolId":339, "schoolName":"atguigu" },
"subjectList":[ { "subjectName":"java", "subjectScore":50 }, {
"subjectName":"PHP", "subjectScore":35 }, { "subjectName":"python",
"subjectScore":24 } ], "teacherMap":{ "aaa":{ "teacherName":"zhangsan",
"teacherAge":20 }, "bbb":{ "teacherName":"zhangsanfeng", "teacherAge":108 },
"ccc":{ "teacherName":"zhangwuji", "teacherAge":25 } } }; //
2.将JSON对象转换为JSON字符串★ var jsonStr = JSON.stringify(student); //
3.发送Ajax请求 $.ajax({
"url":"${pageContext.request.contextPath}/ajax/exer/three", "type":"post",
"dataType":"text", "data":jsonStr, // 这里我们发送JSON字符串作为请求体
"contentType":"application/json;charset=UTF-8", // 这里必须设置请求体的内容类型
"success":function (response) { console.log(response); }, "error":function
(response) { console.log(response); } }); });

在 AjaxController 类下增加方法

1
2
3
4
5
6
7
8
9
10
11
12
// @ResponseBody注解。这个注解的作用是把当前handler方法的返回值直接作为响应数据返回给浏览器而不是进行视图名称的解析。
@RequestMapping("/ajax/exer/three")
public String ajaxExerThree(

// 使用@RequestBody注解标记后,SpringMVC会自动将请求体的JSON数据转换为这里需要的Java实体类对象
// @RequestBody注解也需要jackson的支持
@RequestBody Student student) {

System.out.println("student = " + student);

return "success";
}

结果:

需要注意的点:

  • Ajax 程序:
    • 请求方式必须是 post
    • 必须把 JSON 对象转换为 JSON 字符串
    • 必须把 JSON 字符串赋值给 data 属性
    • 请求体的内容类型(contentType 属性)必须是:application/json;charset=UTF-8
  • handler 方法:
    • 必须将 jackson 的 jar 包导入进来
    • 必须使用@RequestBody 属性修饰 handler 方法形参位置的实体类对象

四、伪静态技术

在 DispatcherServlet 配置中把 url-pattern 设置为*.html,就要求所有普通请求必须以 html 作为扩展名。这样一来,访问 SpringMVC 的请求表面上看起来像是访问 HTML 静态页面,这样的效果我们称之为:伪静态。

伪静态的好处:

  • 有利于 SEO 优化,让搜索引擎更容易找到我们的网站。
  • 隐藏后端具体实现细节,让外界不容易猜到我们后端使用的技术体系,从而给黑客入侵增加难度。

但是如果要求请求扩展名为 html,但是实际上服务器端返回 JSON 数据,就会出现 406 错误,表示:请求的数据格式和实际返回的数据格式不一致,“挂羊头,卖狗肉”。

解决办法有两种:

  • 方案 A:增加和实际返回数据格式一致的 url-pattern
  • 方案 B:增加 HTTP 协议中没有定义的请求扩展名
1
2
3
4
5
6
7
8
9
10
11
12
13
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--<url-pattern>/</url-pattern>-->

<!-- 伪静态效果 -->
<url-pattern>*.html</url-pattern>

<!-- 为Ajax请求返回JSON格式数据准备的 -->
<url-pattern>*.json</url-pattern>

<!-- HTTP协议中没有定义的扩展名 -->
<url-pattern>*.atguigu</url-pattern>
</servlet-mapping>