# AJAX 和 Spring MVC
TIP
- 开始本章节前,务必复习下前两章内容!
- 本章节仅以 GET 和 POST 请求方式演示,不涉及 RESTful 风格的其它请求方式。
# 1. AJAX 请求和 Spring MVC 的参数绑定
# AJAX 发送简单类型参数
这种情况下,和发送 application/x-www-form-urlencoded 情况是一样的。
Spring MVC 该怎么执行参数绑定,就怎么执行参数绑定。
$.ajax({
...
data: {
username: 'tom',
password: '123'
},
...
});
# ajax()
发送简单类型数组的一个坑
类似于表单元素 checkbox 的那种情况,有时候,你需要通过 $.ajax()
向后台传递同一个 key 的多个 value 。
var nums = [1, 2, 3];
var str = $.param({"xxx": nums});
console.info(str); // 注意此处的输出!
$.ajax({
...
data: str,
...
});
这种情况,本质上和普通的请求中的 checkbox 有『很大但又很不起眼』的不同,jQuery 会在请求参数字符串的 key 的名字中加上 %5b%5d
,其实就是 []
。
因此,在 SpringMVC 的 @RequestParam 中指明的请求参数并不是 xxx
而应该是 xxx[]` !例如:
public void demo(@RequestParam("xxx[]") Integer[] prodNums) {
...
}
# ajax 发送 application/json 参数类型的请求
如果,我们将 contentType 赋值为 application/json
表示向后台发起请求时,是将一个 JSON 格式的字符串 携带在了 Request 的 body 部分,需要 Spring MVC 通过 @RequestBody 进行参数绑定,获取并解析出这个 JSON 格式字符串。此时使用 @RequestParam 注解无效。
TIP
Spring MVC 在这里有个小【坑】: Spring MVC 默认使用 Jackson 来做对象和 JSON 格式字符串的互转。Spring MVC 中已内置了相关的配置,无需你再在配置文件中进行配置。 但是 spring-webmvc 的依赖关系中并没有包含 jackson 包!!! 所以,尽管不需要进行配置,但是你的项目的 pom.xml 中,仍然需要你手动加包。
例如,向后台传递一个对象的数组:
var emp1 = { empno: 20, ename: 'tom' };
var emp2 = { empno: 19, ename: 'ben' };
var arr = [emp1, emp2];
var jsonStr = JSON.stringify(arr); // [{"empno":20,"ename":"tom"},{"empno":21,"ename":"jerry"}]
$.ajax({
...
contentType: "application/json",
data: jsonStr, // 一定要传入自己转换好的 JSON 格式字符串。直接传入对象的话,jQuery 【帮】你转成了 queryString
...
});
public void add(@RequestBody Employee[] emps) {
}
# 2. Spring MVC 响应 AJAX 请求
Spring MVC 响应 AJAX 请求,回给客户端浏览器一个 JSON 格式字符串,这很容易实现。
只需要使用 @ResponseBody 注解,标注在请求处理方法上即可。
@RequestMapping("/hello.do")
@ResponseBody
public Student hello() {
Student tom = new Student(1, "tom", 20);
return tom;
}
当你使用了 @ResponseBody 注解,Spring MVC 会【帮】你做两件事情:
将这个方法的返回值(默认使用jackson)转换为 json 格式字符串。
在底层执行
resp.setContentType("application/json");
将 HTTP 响应的 content-type 设置为 application/json 。
此时,你的方法的返回值不再是 ModelAndView,或者是 String。它直接就是代表你所要返回的数据的对象,或对象的集合(通常是 List)。
# 3. @RestController 注解
如果你的一个 Controller 类下的所有请求处理方法都返回的是 JSON 格式字符串,而不是逻辑视图名。那么,你的所有的请求处理方法的『头』上一定都加了 @ResponseBody
注解。
对于这种情况,Spring MVC 提供了一个 @RestController
注解用于替换 @Controller
注解,标注在 Controller 类上。用以表明这个类是一个纯粹的基于 RESTful 风格的 Web 服务。
这样,你就可以不用在这个 Controller 类下的每个方法上添加 @ResponseBody
了。
# 4. 背后功臣 HttpMessageConverter
你之所以能在 Controller 中收、发 JSON 格式字符串,正是因为有名为 HttpMessageConverter 的部件在为 @RequestBody 和 @ResponseBody『服务』的!注意,它与 @RequestParam 无关
@RequestBody 需要利用 HttpMessageConverter 来从 HTTP 的请求 body 中取数据;
@ResponseBody 需要利用 HttpMessageConverter 来往 HTTP 的响应的 body 中放数据。
在这里,你可能会用到 2 种不同的 HttpMessageConverter(取决于你的参数和返回值类型):
StringHttpMessageConverter :
在 Controller 接受、处理请求时,如果你想将请求体中的 JSON String 形式的请求参数字符串作为一个整体取出来,赋给 Controller 的一个 String 类型参数(该参数必然标注了 @RequestBody),此时,StringHttpMessageConverter 实现了这个工作。
在 Controller 准备返回数据,响应请求发起方时,如果,你自己已经在代码逻辑中准备好了 JSON 格式字符串,接下来只需要将它(JSON 格式字符串)放入 HTTP 响应体中,此时,StringHttpMessageConverter 实现了这个工作。
MappingJackson2HttpMessageConverter:
在 Controller 接受、处理请求时,如果你想将请求体中的 JSON String 形式的请求参数字符串取出来,并希望『有人』能帮你把它转换成一个 JavaBean,再赋给 Controller 的一个引用类型参数(该参数必然标注了 @RequestBody),此时,MappingJackson2HttpMessageConverter 实现了这个工作。
在 Controller 准备返回数据,响应请求发起方时,如果,你自己在代码逻辑中准备好了要返回的数据:JavaBean(或 JavaBean 的集合),并希望『有人』帮你把它转换成 JSON 格式字符串,然后再放入 HTTP 响应体中,此时,MappingJackson2HttpMessageConverter 实现了这个工作。
再次强调
无论是上述哪个 HttpMessageConverter 再工作,前提都是你使用了 @RequestBody 和 @ResponseBody 注解。
# 5. StringHttpMessageConverter 乱码问题(了解)
通常,我们不会取直接收、发 JSON 格式字符串,所以,我们用到 MappingJackson2HttpMessageConverter 的机会会比 StringHttpMessageConverter 要多。毕竟,能偷懒为什么不偷懒。
不过,万一因为某种原因,你要直接收、发 JSON 格式字符串,从而利用到 StringHttpMessageConverter 时,要注意,它有个小坑:它默认使用的是 iso-8859-1 编码(也就是 latin-1),因此不支持中日韩文。
提示
好在 MappingJackson2HttpMessageConverter 没有这个问题,它的默认的编码是 UTF-8 。
// 在页面上你看到的是 `????`,而非 `酒店查询`。
@RequestMapping("/test")
@ResponseBody
public String demo() {
return "{\"status\":0,\"errmsg\":null,\"data\":{\"query\":\"酒店查询\",\"num\":65544,\"url\":\"www.test.com\"}}";
}
这种情况下,需要你取显示配置 StringHttpMessageConverter,并在配置中指定它要使用的编码。
代码配置版:SpringWebConfig.java
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // Charset charset = StandardCharsets.UTF_8; Charset charset = Charset.forName("UTF-8"); StringHttpMessageConverter converter = new StringHttpMessageConverter(charset); converters.add(converter); }
.xml 配置文版:spring-web.xml
<bean id="utf8Charset" class="java.nio.charset.Charset" factory-method="forName"> <constructor-arg value="UTF-8"/> </bean> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg ref="utf8Charset"/> </bean> </mvc:message-converters> </mvc:annotation-driven>
补充,你可以将 utf8Charset 的 bean 配置『嵌』在 StringHttpMessageConverter 的 bean 配置里面。因为,除了它,也没别的地方用到了 utf8Charset Bean。
<constructor-arg> <bean id="utf8Charset" class="java.nio.charset.Charset" factory-method="forName"> <constructor-arg value="UTF-8"/> </bean> </constructor-arg>
『完』