【springmvc学习第九弹】对Ajax的支持
最直接的Ajax处理
只要在Controller的方法里面,直接使用response输出你要返回的的Ajax数据,然后return null就可以了,示例如下:
Controller示例
1 @RequestMapping(value = "/hello") 2 public ModelAndView handleRequest(UserModel um,HttpServletResponse response) throws IOException { 3 response.setCharacterEncoding("utf-8"); 4 response.getWriter().println("{uuid:'"+um.getUuid()+"',name:'"+um.getName()+"'}"); 5 return null; 6 }
客户端示例,使用jQuery
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <head> 3 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 4 </head> 5 <script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script> 6 <script language="javascript"> 7 $().ready(function(){ 8 $.getJSON('/mvcexample/hello',{uuid:'1',name:'test'},function(data){ 9 alert(data.uuid+" , "+data.name); 10 }); 11 }); 12 </script> 13 </html>
然后就可以启动服务器测试了。
数据绑定@RequestBody/@ResponseBody
@RequestBody
功能:用于将HttpServletRequest的getInputStream()内容绑定到入参
例子:
1 @RequestMapping(value = "/hello") 2 public byte[] handleRequest(@RequestBody String body)
@ResponseBody
功能:用于将方法的返回值作为响应体
例子:
@RequestMapping(value = “/hello") @ResponseBody public byte[] handleRequest(@RequestBody String body)
注意:他们都只能访问报文体,不能访问报文头
使用@RequestBody/@ResponseBody来支持Ajax
可以使用@RequestBody来自动获取Ajax上传的数据,同时也可以使用@ResponseBody,把要返回的对象自动拼成JSON的格式返回
当然,需要加入几个jackson的包,这里加入了:
jackson-core-2.1.2.jar、jackson-annotations-2.1.2.jar、jackson-databind-2.1.2.jar
测试使用的Controller的方法:
1 @RequestMapping(value = "/hello") 2 @ResponseBody 3 public UserModel handleRequest(@RequestBody String reqBody, UserModel um) { 4 System.out.println("the reqBody="+reqBody); 5 um.setName(um.getName()+",server"); 6 return um; 7 }
测试使用的页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 3 </head> 4 <script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script> 5 <script language="javascript"> 6 $().ready(function(){ 7 $.getJSON('/mvcexample/hello',{uuid:'1',name:'test'},function(data){ 8 alert(data.uuid+" , "+data.name); 9 }); 10 }); 11 </script>
去测试看看吧,可以看到Controller的方法直接返回一个um对象,但是 @ResponseBody会把这个对象自动变成json格式的,再传回客户端,非常方便。
当然, @ResponseBody也支持集合对象自动变成json格式,比如:
测试使用的Controller方法
1 @RequestMapping(value = "/hello") 2 @ResponseBody 3 public List<UserModel> handleRequest(@RequestBody String reqBody, UserModel um) { 4 System.out.println("the reqBody="+reqBody); 5 um.setName(um.getName()+",server"); 6 List<UserModel> list = new ArrayList<UserModel>(); 7 list.add(um); 8 UserModel um2 = new UserModel(); 9 um2.setUuid("22"); 10 um2.setName("222"); 11 list.add(um2); 12 return list; 13 }
测试使用的页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head> 3 <script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script> 4 <script language="javascript"> 5 $().ready(function(){ 6 $.getJSON('/mvcexample/hello',{uuid:'1',name:'test'},function(data){ 7 $.each(data,function(index,v){ 8 alert("tr="+v.uuid+",v="+v.name); 9 }); 10 }); 11 }); 12 </script>
使用HttpEntity/ResponseEntity来支持Ajax
使用HttpEntity/ResponseEntity不但能访问到报文体,还可以访问报文头
示例,主要的变化在Controller的方法上,页面不用变化,如下:
1 @RequestMapping(value = "/hello") 2 public ResponseEntity<List<UserModel>> handleRequest(HttpEntity<String> req, UserModel um) { 3 System.out.println("req headers="+req.getHeaders()+", reqBody="+req.getBody()); 4 5 um.setName(um.getName()+",server"); 6 List<UserModel> list = new ArrayList<UserModel>(); 7 list.add(um); 8 UserModel um2 = new UserModel(); 9 um2.setUuid("22"); 10 um2.setName("222"); 11 list.add(um2); 12 13 ResponseEntity<List<UserModel>> ret = new ResponseEntity<List<UserModel>>(list,HttpStatus.OK); 14 return ret; 15 }
对Ajax返回xml的支持
前面的Ajax使用的是json格式,下面看看对xml的支持
要让Spring Web MVC支持xml格式,需要加入如下jar包:
jaxb-api-2.2.5.jar,jaxb-impl-2.2.5.jar
在要返回的对象头上使用如下注解,例如:
1 @XmlRootElement(name = "testxml") 2 public class UserModel {
这表明返回的xml的根元素名称为testxml,注意:由于xml是单根的,所以只能返回一个对象,而不能返回一个list,如果要返回多条值,可以在这个对象中包含多个其他对象
返回的结果同样用@ResponseBody注解即可,这个注解会根据请求的类型,自动决定是返回json还是xml,当然默认是返回json格式的,如 果要返回xml格式,那么请求的时候,要指定accept=application/xml
示例的Controller方法
1 @RequestMapping(value = "/hello") 2 @ResponseBody 3 public UserModel handleRequest(HttpEntity<String> req, UserModel um) { 4 System.out.println("req headers="+req.getHeaders()+", reqBody="+req.getBody()); 5 6 um.setName(um.getName()+",server"); 7 8 PhoneNumberModel pnm = new PhoneNumberModel("123","321"); 9 PhoneNumberModel pnm2 = new PhoneNumberModel("2222","333"); 10 List<PhoneNumberModel> tempList = new ArrayList<PhoneNumberModel>(); 11 tempList.add(pnm2); 12 tempList.add(pnm); 13 14 um.setPm(tempList); 15 16 return um; 17 }
示例的页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head> 3 <script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script> 4 <script language="javascript"> 5 $().ready(function(){ 6 $.ajax({ 7 url:'/mvcexample/hello', 8 type: 'POST', 9 dataType: 'xml', 10 data: {uuid:'1',name:'test'}, 11 timeout: 1000, 12 error: function(){ alert('Error loading XML document'); }, 13 success: function(xml){ 14 $(xml).find("testxml").children("pm").each(function(i){ 15 var uuid=$(this).children("areaCode").text(); 16 var name=$(this).children("phoneNumber").text(); 17 alert("uuid="+uuid+",name="+name); 18 19 }); } }); }); 20 </script>
返回的xml形如
1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 <testxml> 3 <age>0</age> 4 <name>test,server</name> 5 <pm> 6 <areaCode>2222</areaCode> 7 <phoneNumber>333</phoneNumber> 8 </pm> 9 <pm> 10 <areaCode>123</areaCode> 11 <phoneNumber>321</phoneNumber> 12 </pm> 13 <uuid>1</uuid> 14 </testxml>
HttpMessageConverter
其实前面的程序实现对json和xml的支持,之所以能正常运行,幕后英雄就是HttpMessageConverter,它负责对http传入和传出的内容进行格式转换
比如前面学的@RequestBody是将Http请求正文插入方法中,其实它就会使用适合的HttpMessageConverter将请求体写入某个对象
比如前面学的@ResponseBody是将内容或对象作为Http响应正文返回,使用@ResponseBody将会跳过视图处理部分,直接调用合适的HttpMessageConverter,将返回值写入输出流
现在只要你开启了<mvc:annotation-driven />,它会给AnnotationMethodHandlerAdapter初始化7个转换器,可以通过调用 AnnotationMethodHandlerAdapter类的getMessageConverts()方法来获取转换器的一个集合 List<HttpMessageConverter>,默认开启的有:
1、ByteArrayHttpMessageConverter
2、StringHttpMessageConverter
3、ResourceHttpMessageConverter
4、SourceHttpMessageConverter<T>
5、XmlAwareFormHttpMessageConverter
6、Jaxb2RootElementHttpMessageConverter
7、MappingJacksonHttpMessageConverter
Spring是如何寻找最佳的HttpMessageConverter呢?
最基本的方式就是通过请求的accept里面的格式来匹配,如果accept=application/json之类的,就使用json的 HttpMessageConverter,如果accept=application/xml之类的,就使用xml的 HttpMessageConverter,
内容协商
什么是内容协商
简单点说,就是同一资源,可以有多种表现形式,比如xml、json等,具体使用哪种表现形式,是可以协商的。
这是RESTfull的一个重要特性,Spring Web MVC也支持这个功能。
Spring MVC REST是如何决定采用何种方式(视图)来展示内容呢?
一:根据Http请求的header中的Accept属性的值来判读,比如:
Accept: application/xml 将返回xml格式数据
Accept: application/json 将返回json格式数据
优点:是这种方式是理想的标准方式
缺点:是由于浏览器的差异,导致发送的Accept Header头可能会不一样,从而导致服务器不知要返回什么格式的数据
二:根据扩展名来判断,比如:
/mvc/test.xml 将返回xml格式数据
/mvc/test.json 将返回json格式数据
/mvc/test.html 将返回html格式数据
缺点:丧失了同一URL的多种展现方式。在实际环境中使用还是较多的,因为这种方式更符合程序员的习惯
三:根据参数来判断
/mvc/test?format=xml 将返回xml数据
/mvc/test?format=json 将返回json数据
缺点:需要额外的传递format参数,URL变得冗余繁琐,缺少了REST的简洁风范
使用内容协商的功能,如果不使用第三种方式的话,3.2的版本可以什么都不用配置,默认就能支持前面两种。下面还是看看怎么配置,示例如下:
需要在spring的配置文件中做配置,示例如下:
1 <!--1、检查扩展名(如my.pdf);2、检查Parameter(如my?format=pdf);3、检查Accept Header--> 2 <bean id= "contentNegotiationManager" class= "org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> 3 <!-- 扩展名至mimeType的映射,即 /user.json => application/json --> 4 <property name= "favorPathExtension" value= "true" /> 5 <!-- 用于开启 /userinfo/123?format=json 的支持 --> 6 <property name= "favorParameter" value= "true" /> 7 <property name= "parameterName" value= "format"/> 8 <!-- 是否忽略Accept Header --> 9 <property name= "ignoreAcceptHeader" value= "false"/> 10 <property name= "mediaTypes"> <!--扩展名到MIME的映射;favorPathExtension, favorParameter是true时起作用 --> 11 <value> 12 ccjson=application/json 13 ccxml=application/xml 14 html=text/html 15 </value> 16 </property> 17 <!-- 默认的content type --> 18 <property name= "defaultContentType" value= "text/html" /> 19 </bean> 20 21 <!-- ========================= VIEW定义 ========================= --> 22 <!-- 内容协商视图解析器;根据客户端不同的请求决定不同的view进行响应 --> 23 <!-- 会自动根据解析的contentType来决定使用哪个视图解析器(默认使用整个web应用中的viewResolver) --> 24 <bean class= "org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order= "0"> 25 <!-- 内容协商管理器 用于决定media type --> 26 <property name= "contentNegotiationManager" ref= "contentNegotiationManager"/> 27 <!-- 默认视图 放在解析链最后 --> 28 <property name= "defaultViews"> 29 <list> 30 <bean class= "org.springframework.web.servlet.view.json.MappingJackson2JsonView"/> 31 <bean class= "org.springframework.web.servlet.view.xml.MarshallingView"> 32 <property name= "marshaller"> 33 <bean class= "org.springframework.oxm.jaxb.Jaxb2Marshaller"> 34 <property name= "packagesToScan" value= "cn.javass"></property> 35 </bean> 36 </property> 37 </bean> 38 </list> 39 </property> 40 </bean> 41 42 <!-- bean name view resolver--> 43 <bean class= "org.springframework.web.servlet.view.BeanNameViewResolver" p:order= "1"/> 44 <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用 html)- --> 45 <bean id= "defaultViewResolver" class= "org.springframework.web.servlet.view.InternalResourceViewResolver" p:order= "2"> 46 <property name= "viewClass" value= "org.springframework.web.servlet.view.JstlView"/> 47 <property name= "contentType" value= "text/html"/> 48 <property name= "prefix" value= "/WEB-INF/jsp/"/> 49 <property name= "suffix" value= ".jsp"/> 50 </bean> 51 <!-- 在mvc:annotation-driven里面配置使用内容协商--> 52 <mvc:annotation-driven 53 validator= "validator" 54 conversion-service= "conversionService" 55 content-negotiation-manager= "contentNegotiationManager" 56 > 57 </mvc:annotation-driven> 58
n测试文件的变化:
1:dataType: ‘json’, 这句话可以注掉了
2:在请求的url上添加 后缀,还有参数试试看,注意,前面配置的映射格式是ccjson、ccxml哦,例如:
url:'/mvcexample/hello?format=ccxml',
url:'/mvcexample/hello?format=ccjson',
url:‘/mvcexample/hello.ccxml',
url:‘/mvcexample/hello.ccjson',
应该可以看到根据不同的请求,服务端回返回不同格式的数据。