Spring RESTful服務接收和返回JSON最佳實踐

個人學習參考所用,勿噴!  

 

返回JSON

1) 用Maven構建web項目:

構建過程參考limingnihao的blog(寫得相當的詳細!!!):使用Eclipse構建Maven的SpringMVC項目

註解@ResponseBody可以將結果(一個包含字符串和JavaBean的Map),轉換成JSON。由於Spring是採用對JSON進行了封裝的jackson來生成JSON和返回給客戶端,所以這裏需要添加jackson的相關包。項目的pom.xml配置如下:

Xml代碼  收藏代碼
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.     <groupId>com.watson</groupId>  
  5.     <artifactId>rest-spring</artifactId>  
  6.     <packaging>war</packaging>  
  7.     <version>0.0.1-SNAPSHOT</version>  
  8.     <name>rest-spring Maven Webapp</name>  
  9.     <url>http://maven.apache.org</url>  
  10.       
  11.     <dependencies>  
  12.         <!-- 省略其他配置,具體可以參考附件-->  
  13.         ......  
  14.         <dependency>  
  15.             <groupId>org.codehaus.jackson</groupId>  
  16.             <artifactId>jackson-mapper-asl</artifactId>  
  17.             <version>1.4.2</version>  
  18.         </dependency>  
  19.         <dependency>  
  20.             <groupId>org.codehaus.jackson</groupId>  
  21.             <artifactId>jackson-core-asl</artifactId>  
  22.             <version>1.4.2</version>  
  23.         </dependency>  
  24.     </dependencies>  
  25. </project>  
  

2) 在web.xml配置Spring的請求處理的Servlet,具體設置:

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  5.     version="2.5">  
  6.   
  7.     <display-name>Spring-Rest</display-name>  
  8.     <servlet>  
  9.         <servlet-name>rest</servlet-name>  
  10.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  11.         <init-param>  
  12.             <param-name>contextConfigLocation</param-name>  
  13.             <param-value>/WEB-INF/rest-servlet.xml</param-value>  
  14.         </init-param>  
  15.         <load-on-startup>1</load-on-startup>  
  16.     </servlet>  
  17.   
  18.     <servlet-mapping>  
  19.         <servlet-name>rest</servlet-name>  
  20.         <url-pattern>/</url-pattern>  
  21.     </servlet-mapping>  
  22. </web-app>  

 

3) 在rest-servlet.xml中配置如下:

Xml代碼  收藏代碼
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.     xmlns:context="http://www.springframework.org/schema/context"  
  3.     xmlns:mvc="http://www.springframework.org/schema/mvc"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.     xsi:schemaLocation="  
  6.         http://www.springframework.org/schema/beans       
  7.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8.         http://www.springframework.org/schema/context   
  9.         http://www.springframework.org/schema/context/spring-context-3.0.xsd  
  10.         http://www.springframework.org/schema/mvc  
  11.         http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  12.    
  13.     <context:component-scan base-package="com.mkyong.common.controller" />  
  14.     <mvc:annotation-driven />  
  15.   
  16. </beans>  

 

   爲了解決亂碼問題,需要添加如下配置,並且這裏可以顯示的添加MappingJacksonHttpMessageConverter這個轉換器。

Xml代碼  收藏代碼
  1. <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
  2.     <property name="messageConverters">  
  3.         <list>  
  4.             <bean class="org.springframework.http.converter.StringHttpMessageConverter">  
  5.                 <property name="supportedMediaTypes">  
  6.                     <list>  
  7.                         <value>text/plain;charset=UTF-8</value>  
  8.                     </list>  
  9.                 </property>  
  10.             </bean>  
  11.             <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />  
  12.         </list>  
  13.     </property>  
  14. </bean>  
 

 

4) 編寫自己的服務組件類,使用MVC的annotation風格,使用 @ResponseBody處理返回值。具體代碼如下:

Java代碼  收藏代碼
  1. @RequestMapping("/jsonfeed")  
  2. public @ResponseBody Object getJSON(Model model) {  
  3.     List<TournamentContent> tournamentList = new ArrayList<TournamentContent>();  
  4.     tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "World Cup""www.fifa.com/worldcup/"));  
  5.     tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "U-20 World Cup""www.fifa.com/u20worldcup/"));  
  6.     tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "U-17 World Cup""www.fifa.com/u17worldcup/"));  
  7.     tournamentList.add(TournamentContent.generateContent("中超"new Date(), "中超""www.fifa.com/confederationscup/"));  
  8.     model.addAttribute("items", tournamentList);  
  9.     model.addAttribute("status"0);  
  10.       
  11.     return model;  
  12. }  

 

 5)將運行項目,在瀏覽器中輸入http://[host]:[port]/[appname]/jsonfeed.json,例如樓主的實例中輸入如下:http://localhost:7070/rest-spring/jsonfeed/,得到結果爲:

Json代碼  收藏代碼
  1. {"status":0,"items":[{"name":"World Cup","id":8,"link":"www.fifa.com/worldcup/","author":"FIFA","publicationDate":1334559460940},{"name":"U-20 World Cup","id":9,"link":"www.fifa.com/u20worldcup/","author":"FIFA","publicationDate":1334559460940},{"name":"U-17 World Cup","id":10,"link":"www.fifa.com/u17worldcup/","author":"FIFA","publicationDate":1334559460940},{"name":"Confederations Cup","id":11,"link":"www.fifa.com/confederationscup/","author":"FIFA","publicationDate":1334559460940}]}  

 

這裏我們也可以利用Spring3MVC中對試圖和內容協商的方法來處理返回JSON的情況,下面步驟接上面第2步:

3) 在rest-servlet.xml中對相關進行具體的設置:

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"  
  4.     xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"  
  5.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/aop   
  7.         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
  8.         http://www.springframework.org/schema/beans   
  9.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
  10.         http://www.springframework.org/schema/context   
  11.         http://www.springframework.org/schema/context/spring-context-3.0.xsd   
  12.         http://www.springframework.org/schema/mvc   
  13.         http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd   
  14.         http://www.springframework.org/schema/tx   
  15.         http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
  16.   
  17.     <!-- 自動搜索@Controller標註的類,包括其下面的子包 -->  
  18.     <context:component-scan base-package="com.watson.rest" />  
  19.   
  20.     <!-- 根據客戶端的不同的請求決定不同的view進行響應, 如 /blog/1.json /blog/1.xml -->  
  21.     <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">  
  22.         <!-- 設置爲true以忽略對Accept Header的支持 -->  
  23.         <property name="ignoreAcceptHeader" value="true" />  
  24.           
  25.         <!-- 在沒有擴展名時即: "/blog/1" 時的默認展現形式 -->  
  26.         <property name="defaultContentType" value="text/html" />  
  27.   
  28.         <!-- 擴展名至mimeType的映射,即 /blog.json => application/json -->  
  29.         <property name="mediaTypes">  
  30.             <map>  
  31.                 <entry key="html" value="text/html" />  
  32.                 <entry key="pdf" value="application/pdf" />  
  33.                 <entry key="xsl" value="application/vnd.ms-excel" />  
  34.                 <entry key="xml" value="application/xml" />  
  35.                 <entry key="json" value="application/json" />  
  36.             </map>  
  37.         </property>  
  38.       
  39.         <!-- 用於開啓 /blog/123?format=json 的支持 -->  
  40.         <property name="favorParameter" value="false" />  
  41.         <property name="viewResolvers">  
  42.             <list>  
  43.                 <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />  
  44.                 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  45.                     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />  
  46.                     <property name="prefix" value="/pages" />  
  47.                     <property name="suffix" value=".jsp"></property>  
  48.                 </bean>  
  49.             </list>  
  50.         </property>  
  51.         <property name="defaultViews">  
  52.             <list>  
  53.                 <!-- for application/json -->  
  54.                 <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />  
  55.                 <!-- for application/xml -->  
  56.                 <!--   
  57.                 <bean class="org.springframework.web.servlet.view.xml.MarshallingView">   
  58.                     <property name="marshaller">   
  59.                         <bean class="org.springframework.oxm.xstream.XStreamMarshaller"/>   
  60.                     </property>   
  61.                 </bean>   
  62.                 -->  
  63.             </list>  
  64.         </property>  
  65.     </bean>  
  66. </beans>  

 4)編寫自己的服務組件類,使用MVC的annotation風格,這裏可以不再使用@ResponseBody斷言。具體代碼如下:
Java代碼  收藏代碼
  1. //FINAL   
  2. package com.watson.rest.json;  
  3.   
  4. import org.springframework.stereotype.Controller;  
  5. import org.springframework.ui.Model;  
  6. import org.springframework.web.bind.annotation.RequestMapping;  
  7.   
  8. import com.watson.rest.feeds.TournamentContent;  
  9.   
  10. import java.util.ArrayList;  
  11. import java.util.Date;  
  12. import java.util.List;  
  13.   
  14.   
  15. @Controller  
  16. public class FeedController {  
  17.     @RequestMapping("/jsonfeed")  
  18.     public String getJSON(Model model) {  
  19.         List<TournamentContent> tournamentList = new ArrayList<TournamentContent>();  
  20.         tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "World Cup""www.fifa.com/worldcup/"));  
  21.         tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "U-20 World Cup""www.fifa.com/u20worldcup/"));  
  22.         tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "U-17 World Cup""www.fifa.com/u17worldcup/"));  
  23.         tournamentList.add(TournamentContent.generateContent("FIFA"new Date(), "Confederations Cup""www.fifa.com/confederationscup/"));  
  24.         model.addAttribute("items", tournamentList);  
  25.         model.addAttribute("status"0);  
  26.         return "jsontournamenttemplate";  
  27.     }  
  28. }  

這裏的TournamentContent是自定義的POJO類:
Java代碼  收藏代碼
  1. public class TournamentContent {  
  2.     private static int idCounter = 0;  
  3.     private String author;  
  4.     private Date publicationDate;  
  5.     private String name;  
  6.     private String link;  
  7.     private int id;  
  8.   
  9.     public static TournamentContent generateContent(String author, Date date, String name, String link) {  
  10.         TournamentContent content = new TournamentContent();  
  11.         content.author = author;  
  12.         content.publicationDate = date;  
  13.         content.name = name;  
  14.         content.link = link;  
  15.         content.id = idCounter++;  
  16.   
  17.         return content;  
  18.     }  
  19.       
  20.     //省略getter、setter  
  21. }  

5)將運行項目,在瀏覽器中輸入http://[host]:[port]/[appname]/jsonfeed.json,例如樓主的實例中輸入如下:http://localhost:7070/rest-spring/jsonfeed.json,得到結果爲:
Json代碼  收藏代碼
  1. {"status":0,"items":[{"name":"World Cup","id":8,"link":"www.fifa.com/worldcup/","author":"FIFA","publicationDate":1334559460940},{"name":"U-20 World Cup","id":9,"link":"www.fifa.com/u20worldcup/","author":"FIFA","publicationDate":1334559460940},{"name":"U-17 World Cup","id":10,"link":"www.fifa.com/u17worldcup/","author":"FIFA","publicationDate":1334559460940},{"name":"Confederations Cup","id":11,"link":"www.fifa.com/confederationscup/","author":"FIFA","publicationDate":1334559460940}]}  

至此,Spring RESTful服務返回JSON的實踐基本完成(因爲這裏對EXCEPTION的處理還夠)。個人認爲第一種方式更加適合一般的使用,特別是顯示的添加MappingJacksonHttpMessageConverter這個轉換器和對亂碼的處理。

接收JSON
使用 @RequestBody 註解前臺只需要向 Controller 提交一段符合格式的 JSON,Spring 會自動將其拼裝成 bean。
1)在上面的項目中使用第一種方式處理返回JSON的基礎上,增加如下方法:
Java代碼  收藏代碼
  1. @RequestMapping(value="/add",method=RequestMethod.POST)  
  2. @ResponseBody  
  3. public Object addUser(@RequestBody User user)  
  4. {  
  5.     System.out.println(user.getName() + " " + user.getAge());  
  6.     return new HashMap<String, String>().put("success""true");  
  7. }  
 這裏的POJO如下:
Java代碼  收藏代碼
  1. public class User {  
  2.     private String name;  
  3.     private String age;  
  4.   
  5.     //getter setter  
  6. }  
 
2)而在前臺,我們可以用 jQuery 來處理 JSON。從這裏,我得到了一個 jQuery 的插件,可以將一個表單的數據返回成JSON對象:
Js代碼  收藏代碼
  1. $.fn.serializeObject = function(){  
  2.     var o = {};  
  3.     var a = this.serializeArray();  
  4.     $.each(a, function(){  
  5.         if (o[this.name]) {  
  6.             if (!o[this.name].push) {  
  7.                 o[this.name] = [o[this.name]];  
  8.             }  
  9.             o[this.name].push(this.value || '');  
  10.         }  
  11.         else {  
  12.             o[this.name] = this.value || '';  
  13.         }  
  14.     });  
  15.     return o;  
  16. };  
 
   以下是使用 jQuery 接收、發送 JSON 的代碼:
Js代碼  收藏代碼
  1. $(document).ready(function(){  
  2.     jQuery.ajax({  
  3.         type: 'GET',  
  4.         contentType: 'application/json',  
  5.         url: 'jsonfeed.do',  
  6.         dataType: 'json',  
  7.         success: function(data){  
  8.             if (data && data.status == "0") {  
  9.                 $.each(data.data, function(i, item){  
  10.                     $('#info').append("姓名:" + item.name +",年齡:" +item.age);  
  11.                 });  
  12.             }  
  13.         },  
  14.         error: function(){  
  15.             alert("error")  
  16.         }  
  17.     });  
  18.     $("#submit").click(function(){  
  19.         var jsonuserinfo = $.toJSON($('#form').serializeObject());  
  20.         jQuery.ajax({  
  21.             type: 'POST',  
  22.             contentType: 'application/json',  
  23.             url: 'add.do',  
  24.             data: jsonuserinfo,  
  25.             dataType: 'json',  
  26.             success: function(data){  
  27.                 alert("新增成功!");  
  28.             },  
  29.             error: function(){  
  30.                 alert("error")  
  31.             }  
  32.         });  
  33.     });  
  34. });  
 
但是似乎用Spring這套東西真是個麻煩的事情,相對Jersey對RESTful的實現來看,確實有很多不簡潔的地方。

 

 

參考:

官方文檔:http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch18.html

badqiu的BOLG: 《spring REST中的內容協商(同一資源,多種展現:xml,json,html)》

liuweifeng的BOLG :http://blog.liuweifeng.net/archives/407

Gary Mark等的書籍:《Spring Recipes》2ed

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章