使用 Spring 3 來創建 RESTful Web Services

原文出處:http://www.ibm.com/developerworks/cn/web/wa-spring3webserv/index.html


在 Java 中,您可以使用以下幾種方法來創建 RESTful Web Service:使用 JSR 311(311)及其參考實現Jersey、使用 Restlet 框架和從頭開始開發。Spring 是流行的 Java EE 應用開發框架,現在它的 MVC層也支持 REST 了。本文將介紹使用 Spring 開發 RESTful Web Services 的方法。讀者將瞭解如何使用 Spring API和註釋來開發 RESTful Web Services,以及 Spring 是如何將這個新特性整合到它的框架中的。

2011 年 5 月 23 日

引言

Roy Fielding 是 HTTP 1.0 和 1.1 標準的主要作者之一,2000 年,他在他的博士論文中首次提出了 REST。

通過 REST 風格體系架構,請求和響應都是基於資源表示的傳輸來構建的。資源是通過全局 ID 來標識的,這些 ID 一般使用的是一個統一資源標識符(URI)。客戶端應用使用 HTTP 方法(如,GET、POST、PUT 或 DELETE)來操作一個或多個資源。通常,GET 是用於獲取或列出一個或多個資源,POST 用於創建,PUT 用於更新或替換,而 DELETE 則用於刪除資源。

               例如,GET http://host/context/employees/12345                 將獲取 ID 爲 12345 的員工的表示。這個響應表示可以是包含詳細的員工信息的 XML 或 ATOM,或者是具有更好 UI                 的 JSP/HTML 頁面。您看到哪種表示方式取決於服務器端實現和您的客戶端請求的 MIME 類型。            

               RESTful Web Service 是一個使用 HTTP 和 REST 原理實現的 Web Service。通常,一個 RESTful Web Service 將定義基本資源                 URI、它所支持的表示/響應 MIME,以及它所支持的操作。            

               本文將介紹如何使用 Spring 創建 Java 實現的服務器端 RESTful Web Services。這個例子將使用瀏覽器、curl 和 Firefox 插件 RESTClient                 作爲發出請求的客戶端。您可以 下載 本文所使用的源代碼。            

               本文假定您是熟悉 REST 基本知識的。參考資料 中有更多關於 REST 的信息。            

Spring 3 的 REST 支持

在 Spring 框架支持 REST 之前,人們會使用其他幾種實現技術來創建 Java 的 RESTful Web Services,如 Restlet、RestEasy 和 Jersey。Jersey 是其中最值得注意的,它是 JAX-RS(JSR 311)的參考實現。參考資料 中有更多關於 JSR 311 和 Jersey 的信息。

               Spring 是一個得到廣泛應用的 Java EE 框架,它在版本 3 以後就增加了 RESTful Web Services 開發的支持。雖然,對 REST                 的支持並不是 JAX-RS 的一種實現,但是它具有比標準定義更多的特性。REST 支持被無縫整合到 Spring 的 MVC                 層,它可以很容易應用到使用 Spring 構建的應用中。            

Spring REST 支持的主要特性包括:

  •                    註釋,如 @RequestMapping 和                     @PathVariable,支持資源標識和 URL 映射                

  • ContentNegotiatingViewResolver                 支持爲不同的 MIME/內容類型使用不同的表示方式

  • 使用相似的編程模型無縫地整合到原始的 MVC 層

創建一個示例 RESTful Web Service

               本節中的例子將演示 Spring 3 環境的創建過程,並創建一個可以部署到 Tomcat 中的 “Hello World”                 應用。然後我們再完成一個更復雜的應用來了解 Spring 3 REST 支持的重要概念,如多種 MIME                 類型表示支持和 JAXB 支持。另外,本文還使用一些代碼片斷來幫助理解這些概念。您可以 下載                 本文的所有示例代碼。            

本文假定您已經熟悉 Spring 框架和 Spring MVC。

Hello World:使用 Spring 3 REST 支持

要創建這個例子所使用的開發環境,您需要:

  • IDE:Eclipse IDE for JEE (v3.4+)

  • Java SE5 以上

  • Web 容器:Apache Tomcat 6.0(Jetty 或其他容器也可)

  • Spring 3 框架(v3.0.3 是本文編寫時的最新版本)

  • 其他程序庫:JAXB 2、JSTL、commons-logging

               在 Eclipse 中創建一個 Web 應用,然後設置 Tomcat 6 作爲它的運行環境。然後,您需要設置 web.xml 文件來激活                 Spring WebApplicationContext。這個例子將 Spring bean 配置分成兩個文件:rest-servlet.xml 包含與 MVC/REST                 有關的配置,rest-context.xml 包含服務級別的配置(如數據源 beans)。清單 1 顯示了 web.xml 中的 Spring 配置的部分。            

清單 1. 在 web.xml 中激活 Spring WebApplicationContext
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
	/WEB-INF/rest-context.xml
</param-value>
</context-param>
	
<!-- This listener will load other application context file in addition to 
            rest-servlet.xml -->
<listener>
<listener-class>
	org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
	
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>
	org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>

           在 rest-servlet.xml 文件中創建 Spring MVC 的相關配置(Controller、View、View Resolver)。清單 2 顯示了其中最重要的部分。            

清單 2. 在 rest-servlet.xml 文件中創建 Spring MVC 配置
<context:component-scan base-package="dw.spring3.rest.controller" />

<!--To enable @RequestMapping process on type level and method level-->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

<!--Use JAXB OXM marshaller to marshall/unmarshall following class-->
<bean id="jaxbMarshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
	<list>
	    <value>dw.spring3.rest.bean.Employee</value>
	    <value>dw.spring3.rest.bean.EmployeeList</value>
	</list>
</property>
</bean>

<bean id="employees" class=
	"org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="jaxbMarshaller" />
</bean>

<bean id="viewResolver" class=
"org.springframework.web.servlet.view.BeanNameViewResolver" />

上面的代碼中:

Component-scan
啓用對帶有 Spring 註釋的類進行自動掃描
在實踐中,它將檢查控制器類中所定義的 @Controller 註釋。
DefaultAnnotationHanlderMappingsAnnotationMethodHandlerAdapter
使用 @ReqeustMapping 註釋的類或函數的 beans 由 Spring 處理
這個註釋將在下一節進行詳細介紹。
Jaxb2Mashaller
定義使用 JAXB 2 進行對象 XML 映射(OXM)的編組器(marshaller)和解組器(unmarshaller
MashallingView
定義一個使用 Jaxb2Mashaller 的 XML 表示 view
BeanNameViewResolver
使用用戶指定的 bean 名稱定義一個視圖解析器
本例將使用名爲 “employees” 的 MarshallingView

               這樣就完成了 Spring 的相關配置。下一步是編寫一個控制器來處理用戶請求。清單 3 顯示的是控制器類。            

清單 3. dw.spring3.rest.controller.EmployeeController
@Controller
publicclass EmployeeController {
@RequestMapping(method=RequestMethod.GET, value="/employee/{id}")
public ModelAndView getEmployee(@PathVariable String id) {
	Employee e = employeeDS.get(Long.parseLong(id));
	returnnew ModelAndView(XML_VIEW_NAME, "object", e);
}
}

@RequestMapping 註釋是 Spring REST 特性的關鍵所在。它指定所註釋的方法將處理哪個 HTTP             方法(RequestMethod.GET)和哪個             URI(/employee/{id})。注意:        

  •                對於 {id} 佔位符,使用 @PathVariable 註釋可以將 {}                 內的值注入到函數的參數。            

  • XML_VIEW_NAME 爲 employees,這是 rest-servlet.xml 中定義的視圖名稱。

  • employeeDS 是一個基於內存的數據源,它的實現已經超出本文寫作範圍。

               將 Web 應用發佈到您的 Tomcat 上。這時,您可以打開瀏覽器,然後輸入                 http://<host>:<port>/<appcontext>/service/employee/1。瀏覽器就會顯示一個 ID                爲 1 的員工信息的 XML 視圖。            

請繼續閱讀以瞭解更多關於 Spring REST 支持的特性。

方法

               資源操作是通過 HTTP 方法實現的,如 GET、POST、PUT 和 DELETE。您在前面已經瞭解瞭如何使用                 GET 方法查詢員工信息。現在我們將介紹 POST、PUT 和 DELETE。            

           通過使用 @RequestMapping 註釋的功能,處理不同方法的代碼是非常相似的。清單 4             顯示了 EmployeeController 的代碼片斷。        

清單 4. dw.spring3.rest.controller.EmployeeController
@RequestMapping(method=RequestMethod.POST, value="/employee")
public ModelAndView addEmployee(@RequestBody String body) {
	Source source = new StreamSource(new StringReader(body));
	Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
	employeeDS.add(e);
	returnnew ModelAndView(XML_VIEW_NAME, "object", e);
}

@RequestMapping(method=RequestMethod.PUT, value="/employee/{id}")
public ModelAndView updateEmployee(@RequestBody String body) {
	Source source = new StreamSource(new StringReader(body));
	Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
	employeeDS.update(e);
	returnnew ModelAndView(XML_VIEW_NAME, "object", e);
}

@RequestMapping(method=RequestMethod.DELETE, value="/employee/{id}")
public ModelAndView removeEmployee(@PathVariable String id) {
	employeeDS.remove(Long.parseLong(id));
	List<Employee> employees = employeeDS.getAll();
	EmployeeList list = new EmployeeList(employees);
	returnnew ModelAndView(XML_VIEW_NAME, "employees", list);
}

在上面的代碼中:

  • RequestMethod.<Method> 的值確定所註釋的函數應該處理哪個 HTTP 方法。                

  • 通過 @RequestBody,HTTP 請求的主體內容可以作爲一個參數注入。

    在本例中,主體內容是表示員工信息的 XML 數據。我們使用 JAXB 2 來將 XML 解組爲 Java Bean,然後將它存儲。一個示例請求主體可以是:

    <employee><id>3</id><name>guest</name></employee>


  • 其他可以注入到函數參數的有用的註釋有 @PathVariable@RequestParm 等等。Spring 文檔有完整列表的註釋(見 參考資料)。

資源集合

               通常,您還需要操作批量的資源。例如,您可能希望獲取所有員工的信息而不只是一個員工的信息。您可以採取類似於之前情況的方法實現;您所需要做的修改就是將 URI                 從 /employee 修改成 /employees。員工的複數形式能夠正確反映批量的語義。清單 5 顯示了這種實現方法。            

清單 5. EmployeeController 的 getAllEmployees
@RequestMapping(method=RequestMethod.GET, value="/employees")
public ModelAndView getEmployees() {
	List<Employee> employees = employeeDS.getAll();
	EmployeeList list = new EmployeeList(employees);
	returnnew ModelAndView(XML_VIEW_NAME, "employees", list);
}

               您需要爲 Employee 集合聲明一個包裝類。這個包裝類是 JAXB 2 所需要的,因爲它無法正確地編組 java.util.List                 類。清單 6 顯示了 EmployeeList 類。            

清單 6. dw.spring3.rest.bean.EmployeeList
@XmlRootElement(name="employees")
publicclass EmployeeList {
	privateint count;
	private List<Employee> employees;

	public EmployeeList() {}
	
	public EmployeeList(List<Employee> employees) {
		this.employees = employees;
		this.count = employees.size();
	}

	publicint getCount() {
		return count;
	}
	publicvoid setCount(int count) {
		this.count = count;
	}
	
	@XmlElement(name="employee")
	public List<Employee> getEmployees() {
		return employees;
	}
	publicvoid setEmployees(List<Employee> employees) {
		this.employees = employees;
	}
	
}

內容協商

               REST 服務的另一個常用特性是它們能夠根據請求產生不同的表示。例如,如果客戶端請求所有員工的 HTML/text                 表示方式,那麼服務器就應該爲用戶產生一個符合語法規則的 HTML 頁面。如果客戶端請求的是員工的 application/XML                 表示方式,那麼服務器就應該產生一個 XML 結果。其他受歡迎的表示方式還有 ATOM 和 PDF。            

               Spring 3 引入了一個名爲 ContentNegotiatingViewResolver                 的新視圖解析器。它可以根據請求的內容類型(請求頭中的 Accept 屬性)或 URI                 後綴來切換視圖解析器。下面的例子使用 ContentNegotiatingViewResolver                 來實現多種表示方式的支持。            

               在 rest-servlet.xml 文件中,用註釋去掉原來定義的 viewResolver。而使用 ContentNegotiatingViewResolver                 來替代它,如 清單 7 所示。            

清單 7. 定義內容協商
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
	<map>
	<entry key="xml" value="application/xml"/>
	<entry key="html" value="text/html"/>
	</map>
</property>
<property name="viewResolvers">
	<list>
	<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
	<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
		<property name="viewClass" value=
		"org.springframework.web.servlet.view.JstlView"/>
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	</list>
</property>
</bean>

               這個定義顯示了處理兩種請求內容的支持:application/xml 和                 text/html。這段代碼也定義了兩個視圖解析器:其中 BeanNameViewResolver                 是負責處理 application/xml 的,而另一個 UrlBasedViewResolver                 則負責處理 text/html。            

               在實踐中,當您在瀏覽器上輸入 http://<host>:<port>/<appcontext>/service/employees,那麼它就會請求                 text/html 形式的所有員工信息。然後 UrlBasedViewResolver                  會進行解析,而 Spring 將會選擇 /WEB-INF/jsp/employees.jsp 作爲返回的視圖。當您添加請求頭                 Accept:application/xml 並再發起請求時,那麼 BeanNameViewResolver                 就會進行解析。如 清單 5 中的代碼,它將使用一個名爲 employees                 的視圖來表示,它就是所定義的 JAXB 2 編組器視圖。            

getAllEmployees() 的控制器代碼不需要修改。employees.jsp 將會使用名爲 employees                 的模型對象來渲染。清單 8 顯示了 employees.jsp 的代碼片斷。            

清單 8. /WEB-INF/jsp 中的 employees.jsp
<table border=1>
	<thead><tr>
		<th>ID</th>
		<th>Name</th>
		<th>Email</th>
	</tr></thead>
	<c:forEach var="employee" items="${employees.employees}">
	<tr>
		<td>${employee.id}</td>
		<td>${employee.name}</td>
		<td>${employee.email}</td>
	</tr>
	</c:forEach>
</table>

與 REST 服務通信的客戶端

               現目前爲止,您已經開發一個簡單的支持對員工信息的 CRUD(增刪查改)操作的 RESTful Web                 Service。接下來這一節將介紹如何與這個服務進行通信。您將使用 curl 來測試這個 REST 服務。            

               您也可以使用名爲 RESTClient 的 Firefox 插件來測試 REST 服務。它很容易使用且帶有良好的 UI。參考資料                 有關於它的下載信息。            

使用 curl

               Curl 是一個流行的能以 HTTP 和 HTTPS 協議向服務器發送請求的命令行工具。Curl 是 Linux 和 Mac                 上的一個內置工具。對於 Windows 平臺,您可以另外下載這個工具(見 參考資料)。            

要初始化查詢所有員工信息的第一個 curl 命令,您可以輸入:

curl –HAccept:application/xml 
http://localhost:8080/rest/service/employees

它的響應將是 XML 格式的,並且包含所有的員工信息,如圖 1 所示。

圖 1. XML 方式表示的所有員工信息
包含员工姓名、ID 和电子邮件的 XML 输出

               您也可以在瀏覽器上嘗試訪問相同的 URL。這時,頭信息中的 Accept 爲 text/html,所以瀏覽器會顯示                 employees.jsp 中定義的一個表格。圖 2 顯示的是這種情況。            

圖 2. HTML 方式表示的所有員工信息
有一些数据与图 1 相同,但是这些数据是显示在表格中的

            要將一個新的員工信息 POST 到服務器上,我們可以使用下面的代碼。清單 4              中的 addEmployee() 代碼將會使用請求體並將它解組爲              Employee 對象。            

curl -X POST -HContent-type:application/xml --data
"<employee><id>3</id><name>guest</name><email>[email protected]</employee>"
http://localhost:8080/rest/service/employee

這樣就添加了一個新的員工信息。您可以使用第一個例子來驗證員工列表。

PUT 方法類似於 POST。

curl -X PUT -HContent-type:application/xml --data
"<employee><id>3</id><name>guest3</name><email>[email protected]</employee>" 
http://localhost:8080/rest/service/employee/3

上面的代碼修改了 ID 爲 3 的員工數據。

結束語

               現在 Spring 3 已經在它的 MVC 層上支持 REST 了,您可以使用 Spring API 和註釋來開發 RESTful Web Services                 了。本文的示例向您介紹瞭如何使用一些能夠幫您簡化開發 Java 服務器端 RESTful Web Services 的 Spring 3 的新特性。            


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