分析XML如何实现REST式的SOA

日期: 2008-04-13 作者:Adriaan de Jonge 来源:TechTarget中国

  什么是 SOA?

  如果公司有大量应用程序,这些程序供不同部门的承担不同责任的职员使用,那么就适合使用面向服务体系结构(Service Oriented Architecture,SOA)。这些应用程序可以共享功能,但是功能的组合、用户界面细节和易用性需求是不同的。与许多企业体系结构一样,SOA 也采用一个多层模型,但是它不只如此。在服务器中,功能分散在单独的服务上。一个客户机可以使用其中的一个或多个服务,而一个服务也可以由许多客户机使用。由此形成了一个松散耦合的体系结构,这大大提高了现有软件的可重用性。

  常用的重型实现

  SOA 尤其适合大公司,大公司往往有数百个应用程序,应用程序之间缺少良好的集成,所以公司需要清理 IT 基础结构。SOA 是一种已经证明有效的实践,对于大型环境尤其有效。采用 SOA 的公司可以把遗留的应用程序转换为服务,并把服务集成为现代应用程序的后端。可以使用中间件技术对服务进行组合,并对服务中的特定功能进行访问控制。因为在大型环境中对 SOA 的需求最为强烈,所以中间件技术的厂商通常把产品的重点放在大型和重型解决方案上。

  SOA 和轻量型技术

  SOA 背后的思想对于小公司同样是有价值的。重型解决方案的设置成本和所需的人员技能可能使小公司不敢尝试 SOA — 但这是不应该的。暂且不要考虑重型实现,我们先来考察一下 SOA 的基本概念:

  从现有的或新的应用程序中提取出服务

  把服务集中起来,供许多客户机使用

  并没有什么因素妨碍我们用轻量型技术实现这些思想。您可以先建立一个小型 SOA 并逐渐扩展它。如果您的公司以后发展成大型跨国公司,随时可以迁移到重型技术。

  什么是 REST?

  SOA 通常是用 SOAP 协议实现的,服务由一个 WSDL(Web Services Description Language, Web 服务描述语言)文档来描述。尽管有许多开发工具大大简化了对 SOAP 和 WSDL 的处理过程,但是我仍然把它们看作重型技术,因为如果不使用这些工具,SOAP 和 WSDL 是很难处理的。

  也可以通过超文本传输协议(HTTP)发送简单的消息来实现 SOA。这基本上就是 REST 式 Web 服务 (RESTful Web services) 的工作方式。Representational State Transfer(简称 REST,中文翻译“具象状态传输”。REST 这个名称是由 Roy Fielding 首创的)并不是一个协议或技术;它是一种体系结构风格。REST 是 SOAP 的轻量型替代品,它是面向资源的,而不是面向操作的。它常常被归结为远程过程使用 HTTP 调用 GET、POST、PUT 和 DELETE 语句。我认为,这只是第二个重要的步骤。

  第一个(也是最重要的)步骤是把所有资源建模为 URL 形式。URL 容易记忆,同时能够访问无数 Web 页面。至少,如果建模方式适当的话,很容易记住 URL。如果过分重视 GET、POST、PUT 和 DELETE,就可能产生不容易记忆的 URL。

  在实践中,使用 HTTP 的方法可以进一步限制为 GET 和 POST 两种方法,因为大多数浏览器对它们的支持很完善。可以对 http://domain.com/myresources/new 执行 POST,以替代对 http://domain.com/myresources 执行 PUT;对 http://domain.com/myresources/oldresource/delete 执行 POST,以替代对 http://domain.com/myresources/oldresource 执行 DELETE。

  REST 式的设计过程

  在设计 REST 式 Web 服务时,可以采用以下四个步骤:

  决定资源及其描述性 URL。

  为每个 URL 上的通信选择一种数据格式。

  指定每个资源上的方法。

  指定返回的数据和状态码。

  以下是具体的设计过程。假设您是一家航空公司的开发人员。公司有用于预订航班的软件,还有处理付款(现金和信用卡)的组件。它使用软件跟踪包裹、执行内部资源规划和执行许多其他任务。

  假设机场登记处的职员使用一个客户机应用程序,这个程序访问包裹跟踪服务,还使用一个服务为乘客分配座位。处理包裹的地勤人员只需要包裹跟踪服务,不需要其他服务。他们的客户机只允许他们确认已经登记的包裹是否到达了。不允许他们登记新的包裹。

  在这个示例中,我们将设计包裹跟踪服务。首先,决定资源:旅行者、航班和包裹(注意,在出现 {id} 的任何地方,都可以填写任意数字):

  http://luggagetracking.airlinecompany.com/bags/{id}

  http://luggagetracking.airlinecompany.com/flights/{id}

  http://luggagetracking.airlinecompany.com/travellers/{id}

  为每个资源选择一种数据格式:

  包裹:

         < bag id=”{id}”>
  < traveller id=”{traveller-id}”/>

  < flight id=”{flight-id}” />

  < status>{current-status: departure/plane/arrival}< /status>

  < /bag>
 
        航班:

         < flight id=”{id}”>
  < travellers>

  < traveller id=”{traveller-id-0}” />

  < traveller id=”{traveller-id-1}” />

  < traveller id=”{traveller-id-2}” />

  < /travellers>

  < bags>

  < bag id=”{bag-id-0}” />

  < bag id=”{bag-id-1}” />

  < bag id=”{bag-id-2}” />

  < /bags>

  < /flight>
 
        乘客:

       < traveller id=”{id}”>
  < flight id=”{flight-id}” />

  < bags>

  < bag id=”{bag-id-0}” />

  < bag id=”{bag-id-1}” />

  < bag id=”{bag-id-2}” />

  < /bags>

  < /traveller>
 
  显然,这个模型过于简单了。对于当前的示例,只需要支持两个方法,因此这个模型已经足够了。登记处应该能够为乘客登记新包裹。在把包裹装进飞机时,地勤人员应该能够修改包裹的状态:

  对 http://luggagetrackingairlinecompany.com/travellers/{id}/newbag 执行 POST,返回一个 < bag>XML 结构。

  对 http://luggagetracking.airlinecompany.com/bags/{id}/status/{newstatus} 执行 POST,返回修改后的 XML 结构。

  使用标准的 HTTP 状态作为状态码。成功的操作都会返回 200。如果系统无法根据资源的 ID 找到它,就会返回 404。系统故障导致的任何错误都会返回 500。

  代码示例:URL 映射

  可以使用多种方式把 URL 映射到实现方法。比较先进的方法可能更灵活,应该用在比较大的应用程序中。这个小示例使用最简单的方法:正则表达式。下面是 BagServlet 上的 post 方法示例,它把 URL 参数传递给底层 servlet。可以在本文的下载文件中找到完整的 servlet 代码。注意,这里没有实现实际的底层服务。 以下是该示例:

   protected void doPost(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

  Pattern pattern = Pattern.compile(“^/?.*?/bags/(.*)/status/(.*)$”);

  Matcher matcher = pattern.matcher(request.getRequestURI());

  if(matcher.matches()) {

  String bagId = matcher.group(1);

  String newStatus = matcher.group(2);

  bagService.changeBagStatus(bagId, newStatus);

  }

  }
 
  在调用这个 URL 时,如果成功,就会隐式地返回状态码 200。更有意义的是,代码返回 XML 结构。这个示例使用 XStream API 把 Java? 对象转换成 XML 结构。这个 API 需要的配置非常少,而且主要根据类中的字段名选择元素名。

  这个示例代码使用下面这些简单的类:

  航班:

  package eu.adraandejonge.restfulsoa;
  public class Flight {

  String id;

  public Flight(String id) {

  super();

  this.id = id;

  }

  }
 
  乘客:

  package eu.adraandejonge.restfulsoa;
  public class Traveller {

  private String id;

  public Traveller(String id) {

  super();

  this.id = id;

  }

  }
 
  包裹:

  package eu.adraandejonge.restfulsoa;
  public class Bag {

  private String id;

  private Flight flight;

  private Traveller traveller;

  private String status;

  public Bag(String id, Flight flight, Traveller traveller, String status) {

  super();

  this.id = id;

  this.flight = flight;

  this.traveller = traveller;

  this.status = status;

  }

  }
 
  假设底层的 BagService 返回一个包裹,包裹的航班 ID 是 1,乘客 ID 是 1,状态是 new。请考虑下面的 GET 实现:

   protected void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

  Pattern pattern = Pattern.compile(“^/?.*?/bags/(.*)$”);

  Matcher matcher = pattern.matcher(request.getRequestURI());

  if (matcher.matches()) {

  String bagId = matcher.group(1);

  Bag bag = bagService.retrieveBag(bagId);

  XStream xstream = new XStream();

  xstream.alias(“bag”, Bag.class);

  xstream.alias(“traveller”, Traveller.class);

  xstream.alias(“flight”, Flight.class);

  xstream.useAttributeFor(Bag.class, “id”);

  xstream.useAttributeFor(Traveller.class, “id”);

  xstream.useAttributeFor(Flight.class, “id”);

  String xml = xstream.toXML(bag);

  response.getWriter().write(xml);

  }

  }
 
  在查询这个 URL 时,它会返回以下信息:

  < bag id=”1″>
  < flight id=”1″/>

  < traveller id=”1″/>

  < status>new< /status>

  < /bag>

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐