第九章:對Ajax的支持——深入淺出學Spring Web MVC

最直接的Ajax處理
只要在Controller的方法裏面,直接使用response輸出你要返回的的Ajax數據,然後return null就可以了,示例如下:
Controller示例
@RequestMapping(value = "/hello")
public ModelAndView handleRequest(UserModel um,HttpServletResponse response) throws IOException {
response.setCharacterEncoding("utf-8");
response.getWriter().println("{uuid:'"+um.getUuid()+"',name:'"+um.getName()+"'}");
return null;
}
客戶端示例,使用jQuery
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script>
<script language="javascript">
$().ready(function(){
$.getJSON('/mvcexample/hello',{uuid:'1',name:'test'},function(data){
alert(data.uuid+" , "+data.name);
});
});
</script>
</html>
 
然後就可以啓動服務器測試了。
數據綁定@RequestBody/@ResponseBody
@RequestBody
功能:用於將HttpServletRequest的getInputStream()內容綁定到入參
例子:
@RequestMapping(value = "/hello")
public byte[] handleRequest(@RequestBody String body)
@ResponseBody
功能:用於將方法的返回值作爲響應體
例子:
@RequestMapping(value = “/hello")
@ResponseBody
public byte[] handleRequest(@RequestBody String body)
 
注意:他們都只能訪問報文體,不能訪問報文頭
 
使用@RequestBody/@ResponseBody來支持Ajax
n可以使用@RequestBody來自動獲取Ajax上傳的數據
n同時也可以使用@ResponseBody,把要返回的對象自動拼成JSON的格式返回
n當然,需要加入幾個jackson的包,這裏加入了:
jackson-core-2.1.2.jar、jackson-annotations-2.1.2.jar、jackson-databind-2.1.2.jar
n測試使用的Controller的方法:
@RequestMapping(value = "/hello")
@ResponseBody
public UserModel handleRequest(@RequestBody String reqBody, UserModel um) {
System.out.println("the reqBody="+reqBody);
um.setName(um.getName()+",server");
return um;
}
 
n測試使用的頁面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script>
<script language="javascript">
$().ready(function(){
$.getJSON('/mvcexample/hello',{uuid:'1',name:'test'},function(data){
alert(data.uuid+" , "+data.name);
});
});
</script>
去測試看看吧,可以看到Controller的方法直接返回一個um對象,但是 @ResponseBody會把這個對象自動變成json格式的,再傳回客戶端,非常方便。
當然, @ResponseBody也支持集合對象自動變成json格式,比如:
n測試使用的Controller方法
@RequestMapping(value = "/hello")
@ResponseBody
public List<UserModel> handleRequest(@RequestBody String reqBody, UserModel um) {
System.out.println("the reqBody="+reqBody);
um.setName(um.getName()+",server");
List<UserModel> list = new ArrayList<UserModel>();
list.add(um);
UserModel um2 = new UserModel();
um2.setUuid("22");
um2.setName("222");
list.add(um2);
return list;
}
n測試使用的頁面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>
<script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script>
<script language="javascript">
$().ready(function(){
$.getJSON('/mvcexample/hello',{uuid:'1',name:'test'},function(data){
$.each(data,function(index,v){
alert("tr="+v.uuid+",v="+v.name);
});
});
});
</script>
 
使用HttpEntity/ResponseEntity來支持Ajax
n使用HttpEntity/ResponseEntity不但能訪問到報文體,還可以訪問報文頭
n示例,主要的變化在Controller的方法上,頁面不用變化,如下:
@RequestMapping(value = "/hello")
public ResponseEntity<List<UserModel>> handleRequest(HttpEntity<String> req, UserModel um) {
System.out.println("req headers="+req.getHeaders()+", reqBody="+req.getBody());
 
um.setName(um.getName()+",server");
List<UserModel> list = new ArrayList<UserModel>();
list.add(um);
UserModel um2 = new UserModel();
um2.setUuid("22");
um2.setName("222");
list.add(um2);
 
ResponseEntity<List<UserModel>> ret = new ResponseEntity<List<UserModel>>(list,HttpStatus.OK);
return ret;
}
對Ajax返回xml的支持
前面的Ajax使用的是json格式,下面看看對xml的支持
要讓Spring Web MVC支持xml格式,需要加入如下jar包:
jaxb-api-2.2.5.jar,jaxb-impl-2.2.5.jar
在要返回的對象頭上使用如下註解,例如:
@XmlRootElement(name = "testxml")
public class UserModel {
這表明返回的xml的根元素名稱爲testxml,注意:由於xml是單根的,所以只能返回一個對象,而不能返回一個list,如果要返回多條值,可以在這個對象中包含多個其他對象
返回的結果同樣用@ResponseBody註解即可,這個註解會根據請求的類型,自動決定是返回json還是xml,當然默認是返回json格式的,如果要返回xml格式,那麼請求的時候,要指定accept=application/xml
 
示例的Controller方法
@RequestMapping(value = "/hello")
@ResponseBody
public UserModel handleRequest(HttpEntity<String> req, UserModel um) {
System.out.println("req headers="+req.getHeaders()+", reqBody="+req.getBody());
 
um.setName(um.getName()+",server");
 
PhoneNumberModel pnm = new PhoneNumberModel("123","321");
PhoneNumberModel pnm2 = new PhoneNumberModel("2222","333");
List<PhoneNumberModel> tempList = new ArrayList<PhoneNumberModel>();
tempList.add(pnm2);
tempList.add(pnm);
 
um.setPm(tempList);
 
return um;
}
 
n示例的頁面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>
<script language="javascript" src="/mvcexample/static/js/jquery-1.3.2.min.js"></script>
<script language="javascript">
$().ready(function(){
$.ajax({
      url:'/mvcexample/hello',
      type: 'POST',
      dataType: 'xml',
      data: {uuid:'1',name:'test'},
      timeout: 1000,
      error: function(){    alert('Error loading XML document');       },
      success: function(xml){
      $(xml).find("testxml").children("pm").each(function(i){
             var uuid=$(this).children("areaCode").text();
             var name=$(this).children("phoneNumber").text();
             alert("uuid="+uuid+",name="+name);
            
      });        }    }); });
</script>
n返回的xml形如
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<testxml>
<age>0</age>
<name>test,server</name>
<pm>
<areaCode>2222</areaCode>
<phoneNumber>333</phoneNumber>
</pm>
<pm>
<areaCode>123</areaCode>
<phoneNumber>321</phoneNumber>
</pm>
<uuid>1</uuid>
</testxml>
 
HttpMessageConverter
n其實前面的程序實現對json和xml的支持,之所以能正常運行,幕後英雄就是HttpMessageConverter,它負責對http傳入和傳出的內容進行格式轉換
n比如前面學的@RequestBody是將Http請求正文插入方法中,其實它就會使用適合的HttpMessageConverter將請求體寫入某個對象
n比如前面學的@ResponseBody是將內容或對象作爲Http響應正文返回,使用@ResponseBody將會跳過視圖處理部分,直接調用合適的HttpMessageConverter,將返回值寫入輸出流
n現在只要你開啓了<mvc:annotation-driven  />,它會給AnnotationMethodHandlerAdapter初始化7個轉換器,可以通過調用AnnotationMethodHandlerAdapter類的getMessageConverts()方法來獲取轉換器的一個集合 List<HttpMessageConverter>,默認開啓的有:
ByteArrayHttpMessageConverter
StringHttpMessageConverter
ResourceHttpMessageConverter
SourceHttpMessageConverter<T>
XmlAwareFormHttpMessageConverter
Jaxb2RootElementHttpMessageConverter
MappingJacksonHttpMessageConverter
Spring是如何尋找最佳的HttpMessageConverter呢?
最基本的方式就是通過請求的accept裏面的格式來匹配,如果accept=application/json之類的,就使用json的HttpMessageConverter,如果accept=application/xml之類的,就使用xml的HttpMessageConverter,
 
內容協商
n什麼是內容協商
簡單點說,就是同一資源,可以有多種表現形式,比如xml、json等,具體使用哪種表現形式,是可以協商的。
這是RESTfull的一個重要特性,Spring Web MVC也支持這個功能。
 
nSpring 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的簡潔風範
n
n使用內容協商的功能,如果不使用第三種方式的話,3.2的版本可以什麼都不用配置,默認就能支持前面兩種。下面還是看看怎麼配置,示例如下:
 
n需要在spring的配置文件中做配置,示例如下:
<!--1、檢查擴展名(如my.pdf);2、檢查Parameter(如my?format=pdf);3、檢查Accept Header-->
    <bean id= "contentNegotiationManager" class= "org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <!-- 擴展名至mimeType的映射,即 /user.json => application/json -->
        <property name= "favorPathExtension" value= "true" />
        <!-- 用於開啓 /userinfo/123?format=json 的支持 -->
        <property name= "favorParameter" value= "true" />
        <property name= "parameterName" value= "format"/>
        <!-- 是否忽略Accept Header -->
        <property name= "ignoreAcceptHeader" value= "false"/>
 <property name= "mediaTypes"> <!--擴展名到MIME的映射;favorPathExtension, favorParameter是true時起作用  -->
            <value>
                ccjson=application/json
                ccxml=application/xml
                html=text/html
            </value>
        </property>
        <!-- 默認的content type -->
        <property name= "defaultContentType" value= "text/html" />
    </bean>
 
<!-- ========================= VIEW定義 ========================= -->
<!-- 內容協商視圖解析器;根據客戶端不同的請求決定不同的view進行響應 -->
    <!-- 會自動根據解析的contentType來決定使用哪個視圖解析器(默認使用整個web應用中的viewResolver) -->
    <bean class= "org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order= "0">
        <!-- 內容協商管理器 用於決定media type -->
        <property name= "contentNegotiationManager" ref= "contentNegotiationManager"/>
        <!-- 默認視圖 放在解析鏈最後 -->
        <property name= "defaultViews">
            <list>
           <bean class= "org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
                <bean class= "org.springframework.web.servlet.view.xml.MarshallingView">
                <property name= "marshaller"
<bean class= "org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name= "packagesToScan" value= "cn.javass"></property>
</bean>
</property>
                </bean>
            </list>
        </property>
    </bean>
 
<!-- bean name view resolver-->
    <bean class= "org.springframework.web.servlet.view.BeanNameViewResolver" p:order= "1"/>
    <!-- 默認的視圖解析器 在上邊的解析錯誤時使用 (默認使用 html)- -->
    <bean id= "defaultViewResolver" class= "org.springframework.web.servlet.view.InternalResourceViewResolver" p:order= "2">
        <property name= "viewClass" value= "org.springframework.web.servlet.view.JstlView"/>
        <property name= "contentType" value= "text/html"/>
        <property name= "prefix" value= "/WEB-INF/jsp/"/>
        <property name= "suffix" value= ".jsp"/>
    </bean>
n在mvc:annotation-driven裏面配置使用內容協商
<mvc:annotation-driven
      validator= "validator"
      conversion-service= "conversionService"
      content-negotiation-manager= "contentNegotiationManager"
      >
</mvc:annotation-driven>
 
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',
 
應該可以看到根據不同的請求,服務端回返回不同格式的數據。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章