`
yefriendly
  • 浏览: 38333 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Web服务与SOA(三)下

    博客分类:
  • SSO
阅读更多

转载自:http://fengshen-xia.iteye.com/blog/293853

使用REST协议来实现SOA服务

RESTRepresentation State Transfer(表示层有状态的传输协议)的简称,它是一种Web架构类型,由Roy Fielding2000年在他的博士论文中提出来的。REST的基本思想是如何充分利用HTTP协议的功能,它特别关注以下内容:

(1)     REST关注资源,即,每种服务都应该设计成对资源进行某种操作;

(2)     REST充分利用了HTTP协议中的谓词的功能,它不仅使用我们常用的GETPOST谓词,而且还使用PUTDELETE谓词

在我们前面使用POX-over-HTTP方法完成的的SOA服务中,我们就使用了POST这个HTTP谓词。虽然前面我们仅仅列出了几个服务的源代码,它们都可以使用POST谓词来实现,当然,您也可以使用其它谓词实现这些服务。REST的思想就是,我们为什么不充分利用HTTP协议谓词的功能,把常见的CRUD操作映射到HTTP协议的谓词上,这样,消息交换协议就显得更加清晰。因此,我们可以把CRUD操作和相应的HTTP谓词进行关联,具体关系如下表所示:

 

 

HTTP谓词

CRUD操作

操作表述

POST

CREATE

Save new resources

GET

READ

Read resources

PUT

UPDATE

Modify existing resources

DELETE

DELETE

Delete resources

 

 

请您记住,资源及其对应的值代表了系统的状态,系统状态的转换将遵行以下几个规则:

(1)     POSTPUTDELETE谓词可以改变资源的状态

(2)     GET谓词不能修改资源的状态

(3)     往系统增加新资源时应该使用POST谓词

(4)     修改系统中的资源应使用PUT谓词

(5)     从系统中删除资源应调用DELETE谓词

(6)     系统交换协议应该是无状态的,即本次方法的调用不应依赖于上次的方法调用

这些听起来很有趣,但资源究竟是什么?资源其实就是HTTP四个谓词能覆盖的请求操作的范围。拿我们前面的讨论过的商品对象来说,它就是一个典型的资源实例。您可以看到,在我们的实现代码中(见代码清单5),有一个IF-THEN的判断语句块,这个语句块根据请求中的含有的方法名来执行不同的操作,实际上,我们可以通过使用HTTP谓词,避免这些条件判断。那么,这是如何实现的呢?

如果客户端想新添一个商品,它需要调用insertItem服务,先要为这个新商品创建一个XML文档,然后通过POST方法把这个XML文档发送到服务器端,服务器的应答可以是一个比较通用的一个结果对象。

代码清单9— 通用结果对象Outcome的源代码

@XmlRootElement(name="Outcome")
public class Outcome {
private String retCode;
private String retMessaget;
...
}

服务器端的实现代码也会因此而减少,因为现在我们不需要考虑条件判断语句(请把下面的代码和代码清单5相比较)

代码清单10— REST风格的新建服务的服务器端实现代码

protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
try{
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class, Outcome.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
//Receiving the XML request and transform it into a Java object
Item item = (Item) unmarshaller.unmarshal(request.getInputStream());

System.out.println("Inserting item# "+item.getId());

// ... insert item
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);

Outcome outcome = new Outcome();
outcome.setRetCode("OK");
outcome.setRetMessaget("Item was inserted successfully");
marshaller.marshal(outcome, response.getOutputStream());
}catch (Exception e) {
throw new ServletException(e);
}}

请注意,上面的代码只是REST协议的一个非常简单的手工实现,我们是使用一个简单的Servlet实现的,因为我们想把注意力集中在消息交换协议的一些重要概念上。虽然REST可由多种方法实现,比如JAX-WS(我们在讨论SOAP时会讲到)Axis2都可实现REST

好,现在回到我们的例子,updateItem服务可由与上面类似的代码实现,唯一的区别就是把update服务的实现代码放在ServletdoPut方法中,而不是放在doPost方法中。其实,REST方法还有一点与众不同,我们这里提前说一下。

代码清单11— REST风格的新建服务的客户器端实现代码

Item item = new Item();
item.set...

//Prepare and establish the connection with the service
URL url = new URL("http://localhost/SoaBookREST/itemService");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
//Set the HTTP request method
con.setRequestMethod("POST");
con.connect();
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class, Outcome.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);

//Send the XML request to the service
marshaller.marshal(item, con.getOutputStream());

//Get the XML response from the service and deserialize it
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Outcome outcome = (Outcome)
unmarshaller.unmarshal(con.getInputStream());

至于deleteItemfindItemByIdfindAllItems服务,REST方法使用的消息交换协议会有些不同,这些操作实际上不需要上传XML到服务器,它们最多只需要传给服务器一个Id号,像findAllItems根本就不要传给服务器任何输入。在这些情况下,URIHTTP谓词就能提供服务器需要的所有信息。下表列出了REST请求的一些URI实例:

 

谓词

URI示例

操作

DELETE

http://localhost/SoaBookREST/itemService/14

删除第14号商品

GET

http://localhost/SoaBookREST/itemService/14

获取第14号商品

GET

http://localhost/SoaBookREST/itemService

获取所有商品

 

您会从上表看到,HTTP请求由谓词加URI构成,它清楚地告诉服务器它要求哪个服务。请您注意,上面的URI包含了一些额外数据(比如Id),因此您需要web描述文件中配置相应的URL映射模式,而不是针对某个具体的URL进行配置,web.xml的示例配置如下:

代码清单12— web.xmlservlet的映射配置

<servlet-mapping>
<servlet-name>ItemService</servlet-name>
<url-pattern>/itemService/*</url-pattern>
</servlet-mapping>

下面是最后两个操作的服务实现代码:

代码清单13— REST风格读取服务的实现

protected void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
{
try{
if (request.getPathInfo()==null){
//findAllItems
ItemList itemList = new ItemList();
itemList.setList(new ArrayList());

//retrieve all items
...
itemList.getList().add(...);
...

//Send the XML message to the client
JAXBContext jaxbContext = JAXBContext.newInstance(ItemList.class, Item.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
marshaller.marshal(itemList, response.getOutputStream());
} else {

//findItemById
int id = (new Integer(request.getPathInfo().substring(1))).intValue();

//retrieve item by id (e.g. from a database)
Item item = ...
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
marshaller.marshal(item, response.getOutputStream());
}
}catch (Exception e) {
throw new ServletException(e);
}}

如果您想更新一个商品,您需要调用如下的REST风格的URI,其中包含了商品的Id信息。

PUT http://localhost/SoaBookREST/itemService/14

其实,REST的这种调用哲学和HTTP请求的自解释性的特点是一致的,上面的URI可以读作“更新14号商品”。

一般说来,如果您要使用上面的方法创建非CRUD服务,您有以下两种选择:

(1)     使用合适的谓词,新建一个Servelet

(2)     重用已有的Servlet和谓词,但需要添加新的代码,以解析和组装HTTP请求中的业务逻辑。

您也可以考虑在请求中加入参数这种比较实用的办法,请求中添加的参数可以让执行的业务流程发生变化,虽然REST的追随者不喜欢这样做。这种方法的请求格式可能如下所示:

http://localhost/SoaBookREST/itemService?id=14

REST追随者们对上面的办法可能颇有微词,实际上,上面的这种请求格式依赖于具体的参数名(id),这多少为REST这种简单线性的请求风格增添了复杂性。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics