REST 基础(二):Web 服务编程,REST 与 SOAP

应用场景介绍(在线用户管理)

本文将借助于一个应用场景,通过基于 REST 和 SOAP Web 服务的不同实现,来对两者进行对比。该应用场景的业务逻辑会尽量保持简单且易于理解,以有助于把我们的重心放在 REST 和 SOAP Web 服务技术特质对比上。

需求描述
这是一个在线的用户管理模块,负责用户信息的创建,修改,删除,查询。用户的信息主要包括:

用户名(唯一标志在系统中的用户)
头衔
公司
EMAIL
描述
需求用例图如下:
REST 基础(二):Web 服务编程,REST 与 SOAP

如图 1 所示,客户端 1(Client1)与客户端 2(Client2)对于信息的存取具有不同的权限,客户端 1 可以执行所有的操作,而客户端 2 只被允许执行用户查询(Query User)与用户列表查询(Query User List)。关于这一点,我们在对 REST Web 服务与 SOAP Web 服务安全控制对比时会具体谈到。下面我们将分别向您介绍如何使用 REST 和 SOAP 架构实现 Web 服务。

使用 REST 实现 Web 服务

本部分将基于 Restlet 框架来实现该应用。Restlet 为那些要采用 REST 结构体系来构建应用程序的 Java 开发者提供了一个具体的解决方案。关于更多的 Restlet 相关内容,本文不做深入讨论,请见参考资源列表。

设计
我们将采用遵循 REST 设计原则的 ROA(Resource-Oriented Architecture,面向资源的体系架构)进行设计。ROA 是什么?简单点说,ROA 是一种把实际问题转换成 REST 式 Web 服务的方法,它使得 URI、HTTP 和 XML 具有跟其他 Web 应用一样的工作方式。

在使用 ROA 进行设计时,我们需要把真实的应用需求转化成 ROA 中的资源,基本上遵循以下的步骤:

分析应用需求中的数据集。
映射数据集到 ROA 中的资源。
对于每一资源,命名它的 URI。
为每一资源设计其 Representations。
用 hypermedia links 表述资源间的联系。
接下来我们按照以上的步骤来设计本文的应用案例。

在线用户管理所涉及的数据集就是用户信息,如果映射到 ROA 资源,主要包括两类资源:用户及用户列表。用户资源的 URI 用 http://localhost:8182/v1/users/{username} 表示,用户列表资源的 URI 用 http://localhost:8182/v1/users 表示。它们的 Representation 如下,它们都采用了如清单 1 和清单 2 所示的 XML 表述方式。
清单 1. 用户列表资源 Representation
Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. <?xml version= "1.0"  encoding= "UTF-8"  standalone= "no" ?>  
  2. <users>  
  3.     <user>  
  4.             <name>tester</name>  
  5.             <link>http://localhost:8182/v1/users/tester</link>   
  6.     </user>  
  7.     <user>  
  8.             <name>tester1</name>  
  9.             <link>http://localhost:8182/v1/users/tester1</link>   
  10.     </user>  
  11. </users>  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<users>
	<user>
			<name>tester</name>
			<link>http://localhost:8182/v1/users/tester</link>
	</user>
	<user>
			<name>tester1</name>
			<link>http://localhost:8182/v1/users/tester1</link>
	</user>
</users>


清单 2. 用户资源 Representation
Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. <?xml version= "1.0"  encoding= "UTF-8"  standalone= "no" ?>  
  2. <user>  
  3.     <name>tester</name>  
  4.     <title>software engineer</title>  
  5.     <company>IBM</company>  
  6.     <email>tester@cn .ibm.com</email>  
  7.     <description>testing!</description>  
  8. </user>  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<user>
	<name>tester</name>
	<title>software engineer</title>
	<company>IBM</company>
	<email>[email protected]</email>
	<description>testing!</description>
</user>


客户端通过 User List Resource 提供的 LINK 信息 ( 如 : <link>http://localhost:8182/v1/users/tester</link> ) 获得具体的某个 USER Resource

Restful Web 服务架构

首先给出 Web 服务使用 REST 风格实现的整体架构图,如下图所示:


图 2. REST 实现架构

REST 基础(二):Web 服务编程,REST 与 SOAP
接下来,我们将基于该架构,使用 Restlet 给出应用的 RESTful Web 服务实现。

下面的章节中,我们将给出 REST Web 服务实现的核心代码片段。关于完整的代码清单,读者可以通过资源列表下载。

客户端实现
清单 3 给出的是客户端的核心实现部分,其主要由四部分组成:使用 HTTP PUT 增加、修改用户资源,使用 HTTP GET 得到某一具体用户资源,使用 HTTP DELETE 删除用户资源,使用 HTTP GET 得到用户列表资源。而这四部分也正对应了图 2 关于架构描述的四对 HTTP 消息来回。关于 UserRestHelper 类的完整实现,请读者参见本文所附的代码示例


清单 3. 客户端实现
Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. public   class  UserRestHelper {  
  2. //The root URI of our ROA implementation.   
  3. public   static   final  tring APPLICATION_URI =  "http://localhost:8182/v1" ;  
  4.   
  5. //Get the URI of user resource by user name.    
  6. private   static  String getUserUri(String name) {  
  7.     return  APPLICATION_URI +  "/users/"  + name;  
  8. }  
  9.   
  10. //Get the URI of user list resource.   
  11. private   static  String getUsersUri() {  
  12.     return  APPLICATION_URI +  "/users" ;  
  13. }  
  14. //Delete user resource from server by user name.   
  15. //使用 HTTP DELETE 方法经由 URI 删除用户资源   
  16. public   static   void  deleteFromServer(String name) {  
  17.     Response response = new  Client(Protocol.HTTP).delete(getUserUri(name));  
  18.     ……   
  19. }  
  20. //Put user resource to server.   
  21. //使用 HTTP PUT 方法经由 URI 增加或者修改用户资源   
  22. public   static   void  putToServer(User user) {  
  23.     //Fill FORM using user data.   
  24.     Form form = new  Form();  
  25.     form.add("user[title]" , user.getTitle());  
  26.     form.add("user[company]" , user.getCompany());  
  27.     form.add("user[email]" , user.getEmail());  
  28.     form.add("user[description]" , user.getDescription());  
  29.     Response putResponse = new  Client(Protocol.HTTP).put(  
  30.     getUserUri(user.getName()), form.getWebRepresentation());  
  31.     ……  
  32. }  
  33. //Output user resource to console.   
  34. public   static   void  printUser(String name) {  
  35.     printUserByURI(getUserUri(name));  
  36. }  
  37.   
  38. //Output user list resource to console.   
  39. //使用 HTTP GET 方法经由 URI 显示用户列表资源   
  40. public   static   void  printUserList() {  
  41.     Response getResponse = new  Client(Protocol.HTTP).get(getUsersUri());  
  42.     if  (getResponse.getStatus().isSuccess()) {   
  43.             DomRepresentation result = getResponse.getEntityAsDom();  
  44.  //The following code line will explore this XML document and output   
  45.  //each user resource to console.   
  46.             ……  
  47.     } else  {   
  48.         System.out.println("Unexpected status:" + getResponse.getStatus());   
  49.     }  
  50. }  
  51.   
  52. //Output user resource to console.   
  53. //使用 HTTP GET 方法经由 URI 显示用户资源   
  54. private   static   void  printUserByURI(String uri) {   
  55.     Response getResponse = new  Client(Protocol.HTTP).get(uri);  
  56.     if  (getResponse.getStatus().isSuccess()) {   
  57.         DomRepresentation result = getResponse.getEntityAsDom();  
  58.         //The following code line will explore this XML document and output   
  59.  //current user resource to console.   
  60.  ……  
  61.     } else  {   
  62.         System.out.println("unexpected status:" + getResponse.getStatus());   
  63.     }  
  64. }  
  65. }   
public class UserRestHelper {
//The root URI of our ROA implementation.
public static final tring APPLICATION_URI = "http://localhost:8182/v1";

//Get the URI of user resource by user name. 
private static String getUserUri(String name) {
	return APPLICATION_URI + "/users/" + name;
}

//Get the URI of user list resource.
private static String getUsersUri() {
	return APPLICATION_URI + "/users";
}
//Delete user resource from server by user name.
//使用 HTTP DELETE 方法经由 URI 删除用户资源
public static void deleteFromServer(String name) {
	Response response = new Client(Protocol.HTTP).delete(getUserUri(name));
	…… 
}
//Put user resource to server.
//使用 HTTP PUT 方法经由 URI 增加或者修改用户资源
public static void putToServer(User user) {
	//Fill FORM using user data.
	Form form = new Form();
 	form.add("user[title]", user.getTitle());
 	form.add("user[company]", user.getCompany());
 	form.add("user[email]", user.getEmail());
 	form.add("user[description]", user.getDescription());
	Response putResponse = new Client(Protocol.HTTP).put(
	getUserUri(user.getName()), form.getWebRepresentation());
 	……
}
//Output user resource to console.
public static void printUser(String name) {
	printUserByURI(getUserUri(name));
}

//Output user list resource to console.
//使用 HTTP GET 方法经由 URI 显示用户列表资源
public static void printUserList() {
	Response getResponse = new Client(Protocol.HTTP).get(getUsersUri());
	if (getResponse.getStatus().isSuccess()) { 
			DomRepresentation result = getResponse.getEntityAsDom();
 //The following code line will explore this XML document and output
 //each user resource to console.
			……
	} else { 
	 	System.out.println("Unexpected status:"+ getResponse.getStatus()); 
	}
}

//Output user resource to console.
//使用 HTTP GET 方法经由 URI 显示用户资源
private static void printUserByURI(String uri) { 
	Response getResponse = new Client(Protocol.HTTP).get(uri);
	if (getResponse.getStatus().isSuccess()) { 
 		DomRepresentation result = getResponse.getEntityAsDom();
 		//The following code line will explore this XML document and output
 //current user resource to console.
 ……
 	} else { 
 		System.out.println("unexpected status:"+ getResponse.getStatus()); 
 	}
}
} 




服务器端实现
清单 4 给出的是服务器端对于用户资源类(UserResourc)的实现,其核心的功能是响应有关用户资源的 HTTP GET/PUT/DELETE 请求,而这些请求响应逻辑正对应了 UserRestHelper 类中关于用户资源类的 HTTP 请求。


清单 4. 服务器端实现
Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. public   class  UserResource  extends  Resource {  
  2. private  User _user;  
  3. private  String _userName;  
  4. public  UserResource(Context context, Request request, Response response) {  
  5. //Constructor is here.   
  6. ……  
  7. }  
  8. //响应 HTTP DELETE 请求逻辑   
  9. public   void  delete() {  
  10.     // Remove the user from container.   
  11.     getContainer().remove(_userName);  
  12.     getResponse().setStatus(Status.SUCCESS_OK);  
  13. }  
  14.   
  15. //This method will be called by handleGet.   
  16. public  Representation getRepresentation(Variant variant) {  
  17.  Representation result = null ;  
  18.  if  (variant.getMediaType().equals(MediaType.TEXT_XML)) {     
  19.     Document doc = createDocument(this ._user);  
  20.     result = new  DomRepresentation(MediaType.TEXT_XML, doc);  
  21.  }  
  22.  return  result;  
  23. }  
  24. //响应 HTTP PUT 请求逻辑。   
  25. public   void  put(Representation entity) {  
  26.  if  (getUser() ==  null ) {  
  27.  //The user doesn't exist, create it   
  28.  setUser(new  User());  
  29.  getUser().setName(this ._userName);  
  30.  getResponse().setStatus(Status.SUCCESS_CREATED);  
  31.  } else  {  
  32.     getResponse().setStatus(Status.SUCCESS_NO_CONTENT);  
  33.  }  
  34.  //Parse the entity as a Web form.   
  35.  Form form = new  Form(entity);  
  36.  getUser().setTitle(form.getFirstValue("user[title]" ));  
  37.  getUser().setCompany(form.getFirstValue("user[company]" ));  
  38.  getUser().setEmail(form.getFirstValue("user[email]" ));  
  39.  getUser().setDescription(form.getFirstValue("user[description]" ));   
  40.  //Put the user to the container.   
  41.     getApplication().getContainer().put(_userName, getUser());   
  42. }  
  43. //响应 HTTP GET 请求逻辑。   
  44. public   void  handleGet() {  
  45.     super .handleGet();  
  46.     if ( this ._user !=  null  ) {  
  47.         getResponse().setEntity(getRepresentation(  
  48.                    new  Variant(MediaType.TEXT_XML)));  
  49.         getResponse().setStatus(Status.SUCCESS_OK);   
  50.     } else  {  
  51.         getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);       
  52.     }  
  53. }  
  54. //build XML document for user resource.   
  55. private  Document createDocument(User user) {  
  56.  //The following code line will create XML document according to user info.    
  57.     ……  
  58. }  
  59. //The remaining methods here   
  60. ……  
  61. }  
public class UserResource extends Resource {
private User _user;
private String _userName;
public UserResource(Context context, Request request, Response response) {
//Constructor is here.
……
}
//响应 HTTP DELETE 请求逻辑
public void delete() {
	// Remove the user from container.
	getContainer().remove(_userName);
 	getResponse().setStatus(Status.SUCCESS_OK);
}

//This method will be called by handleGet.
public Representation getRepresentation(Variant variant) {
 Representation result = null;
 if (variant.getMediaType().equals(MediaType.TEXT_XML)) {	
 	Document doc = createDocument(this._user);
 	result = new DomRepresentation(MediaType.TEXT_XML, doc);
 }
 return result;
}
//响应 HTTP PUT 请求逻辑。
public void put(Representation entity) {
 if (getUser() == null) {
 //The user doesn't exist, create it
 setUser(new User());
 getUser().setName(this._userName);
 getResponse().setStatus(Status.SUCCESS_CREATED);
 } else {
 	getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
 }
 //Parse the entity as a Web form.
 Form form = new Form(entity);
 getUser().setTitle(form.getFirstValue("user[title]"));
 getUser().setCompany(form.getFirstValue("user[company]"));
 getUser().setEmail(form.getFirstValue("user[email]"));
 getUser().setDescription(form.getFirstValue("user[description]")); 
 //Put the user to the container.
	getApplication().getContainer().put(_userName, getUser()); 
}
//响应 HTTP GET 请求逻辑。
public void handleGet() {
	super.handleGet();
	if(this._user != null ) {
	    getResponse().setEntity(getRepresentation(
	               new Variant(MediaType.TEXT_XML)));
	    getResponse().setStatus(Status.SUCCESS_OK);	
	} else {
		getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);		
	}
}
//build XML document for user resource.
private Document createDocument(User user) {
 //The following code line will create XML document according to user info. 
	……
}
//The remaining methods here
……
}


UserResource 类是对用户资源类的抽象,包括了对该资源的创建修改(put 方法),读取(handleGet 方法 )和删除(delete 方法),被创建出来的 UserResource 类实例被 Restlet 框架所托管,所有操纵资源的方法会在相应的 HTTP 请求到达后被自动回调。

另外,在服务端,还需要实现代表用户列表资源的资源类 UserListResource,它的实现与 UserResource 类似,响应 HTTP GET 请求,读取当前系统内的所有用户信息,形成如清单 1 所示的用户列表资源 Representation,然后返回该结果给客户端。具体的实现请读者参见本文所附的代码示例。


使用 SOAP 实现 Web 服务


本文对于 SOAP 实现,就不再像 REST 那样,具体到代码级别的实现。本节将主要通过 URI,HTTP 和 XML 来宏观上表述 SOAP Web 服务实现的技术本质,为下一节 REST Web 服务与 SOAP Web 服务的对比做铺垫。
SOAP Web 服务架构
同样,首先给出 SOAP 实现的整体架构图,如下图所示:

图 3. SOAP 实现架构

REST 基础(二):Web 服务编程,REST 与 SOAP
可以看到,与 REST 架构相比,SOAP 架构图明显不同的是:所有的 SOAP 消息发送都使用 HTTP POST 方法,并且所有 SOAP 消息的 URI 都是一样的,这是基于 SOAP 的 Web 服务的基本实践特征。

获得用户信息列表
基于 SOAP 的客户端创建如清单 5 所示的 SOAP XML 文档,它通过类 RPC 方式来获得用户列表信息。


清单 5. getUserList SOAP 消息

Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. <?xml version= "1.0"  encoding= "UTF-8"  standalone= "no" ?>  
  2. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >  
  3.     <soap:Body>  
  4.         <p:getUserList xmlns:p="http://www.exmaple.com" />  
  5.     </soap:Body>  
  6. </soap:Envelope>  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
		<p:getUserList xmlns:p="http://www.exmaple.com"/>
	</soap:Body>
</soap:Envelope>


客户端将使用 HTTP 的 POST 方法,将上述的 SOAP 消息发送至 http://localhost:8182/v1/soap/servlet/messagerouter URI,SOAP SERVER 收到该 HTTP POST 请求,通过解码 SOAP 消息确定需要调用 getUserList 方法完成该 WEB 服务调用,返回如下的响应:


清单 6. getUserListResponse 消息
Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. <?xml version= "1.0"  encoding= "UTF-8"  standalone= "no" ?>  
  2. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >  
  3.     <soap:Body>  
  4.             <p:get  
  5.                 UserListResponse xmlns:p="http://www.exmaple.com" >  
  6.                 <Users>  
  7.                 <username>tester<username>  
  8.                 <username>tester1<username>  
  9.                 ......  
  10.                 </Users>  
  11.                 <p: getUserListResponse >  
  12.     </soap:Body>  
  13. </soap:Envelope>  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
			<p:get
				UserListResponse xmlns:p="http://www.exmaple.com">
				<Users>
				<username>tester<username>
				<username>tester1<username>
				......
				</Users>
				<p: getUserListResponse >
	</soap:Body>
</soap:Envelope>


获得某一具体用户信息

清单 7. getUserByName SOAP 消息
Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. <?xml version= "1.0"  encoding= "UTF-8"  standalone= "no" ?>  
  2. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >  
  3.     <soap:Body>  
  4.      <p:getUserByName xmlns:p="http://www.exmaple.com" >  
  5.                 <username>tester</username>  
  6.                 </p:getUserByName >  
  7.     </soap:Body>  
  8. </soap:Envelope>  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
	 <p:getUserByName xmlns:p="http://www.exmaple.com">
				<username>tester</username>
				</p:getUserByName >
	</soap:Body>
</soap:Envelope>


同样地,客户端将使用 HTTP 的 POST 方法,将上述的 SOAP 消息发送至 http://localhost:8182/v1/soap/servlet/messagerouter URI,SOAP SERVER 处理后返回的 Response 如下
清单 8. getUserByNameResponse SOAP 消息

Java代码  REST 基础(二):Web 服务编程,REST 与 SOAPREST 基础(二):Web 服务编程,REST 与 SOAP
  1. <?xml version= "1.0"  encoding= "UTF-8"  standalone= "no" ?>  
  2. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >  
  3. <soap:Body>  
  4.     <p:getUserByNameResponse xmlns:p="http://www.exmaple.com" >  
  5.             <name>tester</name>  
  6.             <title>software engineer</title>  
  7.             <company>IBM</company>  
  8.             <email>tester@cn .ibm.com</email>  
  9.             <description>testing!</description>  
  10.     </p:getUserByNameResponse>  
  11. </soap:Body>  
  12. </soap:Envelope>  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
	<p:getUserByNameResponse xmlns:p="http://www.exmaple.com">
			<name>tester</name>
			<title>software engineer</title>
			<company>IBM</company>
			<email>[email protected]</email>
			<description>testing!</description>
	</p:getUserByNameResponse>
</soap:Body>
</soap:Envelope>


实际上,创建新的用户,过程也比较类似,在这里,就不一一列出,因为这两个例子对于本文在选定的点上对比 REST 与 SOAP 已经足够了。

REST 与 SOAP 比较:略

实例参加下面附件code_rest.zip
from:http://www.ibm.com/developerworks/cn/webservices/0907_rest_soap/
  • REST 基础(二):Web 服务编程,REST 与 SOAP
  • 大小: 17.1 KB
  • REST 基础(二):Web 服务编程,REST 与 SOAP
  • 大小: 32.1 KB
  • REST 基础(二):Web 服务编程,REST 与 SOAP
  • 大小: 37.4 KB