Web服务与REST架构:安全、消息传递、事务及Ajax应用解析
1. 身份联合与安全概念
身份联合允许第三方对身份信任进行中介。以旅行社为例,它能验证某人是否为其客户的员工,这可能会影响计费和折扣。在安全概念的规范和部署方面,基于SOAP的协议比原生HTTP协议表现更好。不过,这并不意味着HTTP协议无法弥补差距,也不意味着不能将SOAP的安全特性移植到HTTP上。目前,SOAP有许多HTTP所没有的安全相关特性,在实现如旅行代理这样的应用时,这些特性很有用。需要注意的是,这些安全领域并非业余者可以轻易涉足的,个人不应独自尝试为HTTP添加新的安全概念。
资源导向的替代方案则强调,应用的安全性取决于其最薄弱的环节。例如,若仅对信用卡号进行传输加密,却将其简单存储在数据库中,这只会让攻击者将目标转向数据库。安全视角应涵盖整个系统,而非仅关注网络传输的数据。WS - Security规范并非保障数据安全的唯一工具,HTTPS(即传输层安全[TLS],也称为安全套接层[SSL])在实践中已被证明足以保障信用卡信息在网络传输中的安全。XML签名和加密的使用也不限于WS - *相关标准,Atom联合格式标准的第5节展示了如何在Atom文档中使用这些特性,S3也在第3章中展示了如何实现请求签名和访问控制。这些安全方面在RESTful资源导向服务中是可行的,且已得到应用,但目前还未得到广泛推广。资源导向架构的优势在于其简单性和一致性,在构建安全应用时,复杂性和大量接口并非优势。
2. 可靠消息传递
WS - ReliableMessaging标准旨在为应用提供消息传递的保证,如最多一次(AtMostOnce)、至少一次(AtLeastOnce)、恰好一次(ExactlyOnce)或按顺序(InOrder)传递。它定义了一些新的头部信息(即信封上的“贴纸”)来跟踪序列标识符和消息编号,并包含一些重试逻辑。
在这方面,基于SOAP的协议在规范和实现上比原生HTTP更先进。但HTTP在某些情况下并不需要这些额外的“贴纸”。因为几乎所有的HTTP方法都是幂等的,如GET、HEAD、PUT或DELETE操作,如果操作未成功或不确定是否成功,只需重试请求即可。对于幂等操作,最多一次、至少一次和恰好一次传递没有区别。要实现按顺序传递,只需按顺序发送消息,并确保每条消息都能成功发送。唯一非幂等的方法是POST,而SOAP使用的正是POST方法,它通过定义额外的“贴纸”来解决可靠传递问题。在RESTful应用中,若要实现所有操作的可靠消息传递,建议实现“POST Once Exactly”或完全避免使用POST方法。WS - ReliableMessaging标准主要针对RESTful Web服务未涉及的复杂场景,如消息通过多种协议路由到目的地,或源和目的地都是间歇性访问网络的手机。
3. 事务处理
事务的概念简单,但在分布式环境中实现却极具挑战性。例如,将50美元从银行A转移到银行B,整个操作必须要么成功,要么失败。银行A和银行B相互竞争,各自提供独立的Web服务,理想情况是银行A扣款且银行B入账,或者什么都不发生,不希望出现只扣款不入账或只入账不扣款的情况。
事务处理有两种基本方法。WS - AtomicTransaction标准规定了一种常见的算法——两阶段提交。一般来说,这种方法仅适用于相互信任的各方之间,它易于实现,且在现有产品范围内,因此应用最为广泛。另一种方法由WS - BusinessActivity定义,它更贴近实际业务操作。例如,存入一张外国银行的支票时,银行可能会暂时冻结资金,并向外国银行寻求确认。若在冻结期内发现问题,银行会回滚交易;否则,接受支票。若在交易完成后发现问题,银行会创建一个补偿交易来撤销存款。这种方法的重点在于以可审计的方式纠正错误,而非仅仅防止错误发生。
在原生HTTP应用中,很少有能达到这种规范和部署水平的事务处理机制,通常也不需要。在某些情况下,可以将事务作为资源暴露来实现事务系统,若多个Web服务支持这种事务处理,可在其上添加一些基础设施,通过RESTful两阶段提交进行协调。但两阶段提交需要对所协调的服务有一定的控制权和信任度,当需要与竞争银行合作时,这种方法并不适用。SOA架构师认为两阶段提交一般不适用于基于Web服务的交互,对于RESTful Web服务也是如此。当无法控制所协调的服务时,建议使用异步操作来实现WS - BusinessActivity背后的思想。
4. BPEL、ESB和SOA
在上述基础上,还有一些在大型Web服务领域存在争议的概念。
Business Process Execution Language(BPEL)是一种XML语法,可用于描述跨多个参与方的业务流程,这些流程可通过软件和Web服务自动编排。Enterprise Service Bus(ESB)的定义各不相同,但通常包括Web服务请求的发现、负载均衡、路由、桥接、转换和管理等功能。这通常会导致操作与开发分离,使两者更简单易管理。然而,BPEL和ESB的缺点是它们往往会增加与通用第三方中间件的耦合度和依赖性。其优点是在中间件方面有多种选择,从支持良好的开源产品到知名的专有供应商提供的产品都有。
Service - Oriented Architecture(SOA)是一个定义最不明确的术语。有时关于SOA的讨论声称它涵盖了所有REST/HTTP应用,但最终焦点往往会转向本章所描述的大型Web服务标准。不过,SOA的一个值得注意的方面是,目前许多分布式编程方法侧重于远程过程调用,试图使其尽可能接近本地过程调用。而SOA将焦点重新放回接口,特别是跨越机器边界的接口。机器边界通常与信任边界相关,也是消息可靠性容易出现问题的地方,因此应该对其进行研究,而不是将其抽象掉。SOA的其他方面与服务的技术架构无关,可在资源导向环境、远程过程调用服务环境或异构环境中实现,例如“治理”涉及审计和政策合规性,这些“政策”可以是从政府法规到架构原则的任何内容。
5. 结论
REST和Web服务都已成为热门词汇,但很多人对它们的理解并不深入。本章旨在消除一些误解,展示了SOAP带来的价值有限,而WSDL带来的复杂性过高,同时也介绍了资源导向的替代方案。在选择时,如果需要本章中描述的某些仅在SOAP信封上可用的特性,从一开始就选择SOAP路径可能是个不错的基础。另一种选择是采用轻量级方法,遵循“你不会需要它”(YAGNI)原则,仅添加实际需要的功能。若最终发现需要大型Web服务才能提供的某些特性,可将XML表示包装在SOAP信封中,或挑选所需的特性并移植到HTTP头部。鉴于Web已被证明的可扩展性,从简单开始通常是一个安全的选择。
6. Ajax应用作为REST客户端
Ajax应用在过去几年中非常热门,但很多人对其定义并不清楚。正式定义为:Ajax应用是在Web浏览器中运行的Web服务客户端。例如,JavaScript表单验证器和Flash图形演示虽然在浏览器中运行,但它们不进行程序化的HTTP请求,因此不是Ajax应用;而第2章和第3章中编写的独立客户端不在浏览器中运行,也不是Ajax应用。以Gmail为例,登录后可以看到浏览器向mail.google.com的Web服务发送后台请求,并使用新数据更新网页,这正是Web服务客户端的行为。即使Gmail Web服务没有面向公众的名称,且仅供Gmail网页使用,但它仍然是一个Web服务,有像libgmail这样的库可以作为非Ajax客户端访问Gmail Web服务。
7. 从AJAX到Ajax
最初,Ajax被称为AJAX,是“Asynchronous JavaScript And XML”的缩写,但现在它只是一个普通词汇。这是因为AJAX所代表的含义并不一定准确,Ajax作为一种架构风格,不一定需要涉及JavaScript或XML。AJAX中的JavaScript实际上指的是任何能在浏览器端发起HTTP请求的语言,通常是JavaScript,但也可以是ActionScript(在Flash应用中运行)、Java(在小程序中运行)或特定浏览器语言(如Internet Explorer的VBScript)。XML实际上指的是Web服务发送的任何表示格式,只要浏览器端能够理解即可,常见的是XML,因为它易于浏览器解析,且Web服务倾向于提供XML表示,但JSON也很常见,还可以是HTML、纯文本或图像文件等。因此,开发者放弃了AJAX这个缩写,选择使用Ajax这个词,本书在讨论Ajax时,主要以JavaScript和XML为例,但实际上讨论的是一种应用架构。
8. Ajax架构
Ajax架构的工作流程如下:
1. 用户通过浏览器请求应用的主URI。
2. 服务器提供包含嵌入式脚本的网页。
3. 浏览器渲染网页,要么立即运行脚本,要么等待用户通过键盘或鼠标操作触发脚本的某个动作。
4. 脚本向服务器上的某个URI发起异步HTTP请求,用户在请求进行时可以进行其他操作,甚至可能意识不到请求正在发生。
5. 脚本解析HTTP响应,并使用数据修改用户的视图,这可能涉及使用DOM方法更改原始HTML页面的标签结构,或修改Flash应用或Java小程序中的显示内容。
从用户角度看,就好像GUI自己进行了修改。这种架构与客户端GUI应用类似,这也是为什么Ajax应用常被称赞为像桌面应用一样工作。与标准Web应用相比,标准Web应用的事件循环更简单,每次点击或表单提交都会导致整个视图刷新,浏览器获取新的HTML页面并构建全新的GUI元素;而Ajax应用的GUI可以逐步改变,这节省了带宽,减少了对最终用户的心理影响,应用看起来是逐步变化而非突然跳转。然而,Ajax应用的缺点是每个应用状态都使用相同的URI,即用户最初访问的那个URI,这破坏了可寻址性和无状态性。底层Web服务可能是可寻址和无状态的,但最终用户无法为特定状态添加书签,浏览器的“后退”按钮也无法正常工作,就像只暴露单个URI的SOAP + WSDL Web服务一样,该应用不再符合Web的特性。
9. del.icio.us示例
以del.icio.us社交书签应用的API为例,展示一个用JavaScript编写的Ajax客户端。该应用的用户界面使用纯HTML实现,与之前在命令行运行并将数据输出到标准输出的del.icio.us客户端不同。以下是该应用的具体代码和操作步骤:
首先是用户界面部分(Example 11 - 1):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/transitional.dtd">
<!--delicious-ajax.html-->
<!--An Ajax application that uses the del.icio.us web service. This
application will probably only work when saved as a local
file. Even then, your browser's security policy might prevent it
from running.-->
<html>
<head>
<title>JavaScript del.icio.us</title>
</head>
<body>
<h1>JavaScript del.icio.us example</h1>
<p>Enter your del.icio.us account information, and I'll fetch and
display your most recent bookmarks.</p>
<form onsubmit="callDelicious(); return false;">
Username: <input id="username" type="text" /><br />
Password: <input id="password" type="password" /><br />
<input type="submit" value="Fetch del.icio.us bookmarks"/>
</form>
<div id="message"></div>
<ul id="links"></ul>
上述代码创建了一个HTML表单,用户可以输入del.icio.us账户信息,点击提交按钮将触发JavaScript函数
callDelicious
。同时,页面中有一个
div
标签用于显示消息,一个
ul
标签用于显示书签列表。
接着是
setMessage
函数(Example 11 - 2):
<script type="text/javascript">
function setMessage(newValue) {
message = document.getElementById("message");
message.firstChild.textContent = newValue;
}
该函数用于将给定的字符串放入
div
标签中,更新页面上的消息显示。
然后是
callDelicious
函数(Example 11 - 3和Example 11 - 4):
function callDelicious() {
try {
if (netscape.security.PrivilegeManager.enablePrivilege)
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
} catch (e) {
alert("Sorry, browser security settings won't let this program run.");
return;
}
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
var links = document.getElementById("links");
while (links.firstChild)
links.removeChild(links.firstChild)
setMessage("Please wait...");
request = new XMLHttpRequest();
request.open("GET", "https://api.del.icio.us/v1/posts/recent", true,
username, password);
request.onreadystatechange = populateLinkList;
request.send(null);
此函数的操作步骤如下:
1. 尝试获取浏览器的权限以发送请求,如果权限获取失败,弹出警告框并终止程序。
2. 获取用户在表单中输入的账户信息。
3. 清空
ul
标签中的旧链接。
4. 在
div
标签中显示“请等待…”的消息。
5. 创建一个XMLHttpRequest对象,打开一个GET请求,请求的URL为
https://api.del.icio.us/v1/posts/recent
,并设置请求为异步,同时传入用户名和密码。
6. 设置
onreadystatechange
事件处理函数为
populateLinkList
,当请求状态改变时会调用该函数。
7. 发送请求。
最后是
populateLinkList
函数(Example 11 - 5):
function populateLinkList() {
if (request.readyState != 4)
return;
setMessage("Request complete.");
if (netscape.security.PrivilegeManager.enablePrivilege)
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
posts = request.responseXML.getElementsByTagName("post");
setMessage(posts.length + " link(s) found:");
for (var i = 0; i < posts.length; i++) {
post = posts[i];
var link = document.createElement("a");
var description = post.getAttribute('description');
link.setAttribute("href", post.getAttribute('href'));
link.appendChild(document.createTextNode(description));
var li = document.createElement("li");
li.appendChild(link);
document.getElementById("links").appendChild(li);
}
}
该函数的操作步骤如下:
1. 检查请求是否完成,如果未完成则返回。
2. 在
div
标签中显示“请求完成”的消息。
3. 再次尝试获取浏览器的权限。
4. 从响应的XML文档中获取所有的
post
标签。
5. 在
div
标签中显示找到的链接数量。
6. 遍历每个
post
标签,为每个标签创建一个链接,设置链接的
href
属性为标签的
href
属性值,设置链接的文本为标签的
description
属性值。
7. 将链接放入一个
li
标签中,并将
li
标签添加到
ul
标签中。
这个简单的程序展示了Ajax应用的优点和问题,如异步请求、局部更新页面等,但也存在可寻址性和无状态性被破坏的问题。通过这个示例,可以更好地理解Ajax应用在实际中的工作原理和实现方式。
Web服务与REST架构:安全、消息传递、事务及Ajax应用解析
10. 总结与对比
为了更清晰地对比不同技术和概念,下面通过表格进行总结:
| 技术概念 | 优点 | 缺点 | 适用场景 |
| — | — | — | — |
| SOAP | 具有丰富的安全特性和可靠消息传递机制,适用于复杂业务场景 | 复杂性高,与第三方中间件耦合度高 | 对安全和消息可靠性要求高,且信任关系明确的多方交互场景 |
| RESTful架构 | 简单、一致,具有良好的可扩展性 | 部分安全和事务处理机制需额外实现 | 对性能和简单性要求高,资源导向的应用场景 |
| BPEL | 可描述跨多方的业务流程,实现自动编排 | 增加与中间件的耦合度和依赖性 | 复杂业务流程的自动化编排场景 |
| ESB | 提供请求的发现、负载均衡等功能,分离操作与开发 | 增加与中间件的耦合度和依赖性 | 需要对Web服务请求进行集中管理和优化的场景 |
| Ajax | 实现异步请求,局部更新页面,提升用户体验 | 破坏可寻址性和无状态性 | 需要实现类似桌面应用交互体验的Web应用场景 |
11. 技术选择建议
在选择合适的技术时,需要综合考虑多个因素,以下是一些具体的建议:
-
安全需求
:如果对安全要求极高,如涉及金融交易等敏感信息,SOAP的安全特性可能更适合;如果安全需求相对较低,且追求简单性,RESTful架构结合HTTPS也能满足基本安全需求。
-
业务复杂度
:对于复杂的业务流程,涉及多方交互和事务处理,BPEL和ESB可以帮助实现自动化编排和管理;对于简单的资源操作,RESTful架构更为合适。
-
用户体验
:如果希望提供类似桌面应用的交互体验,减少页面刷新,Ajax是一个不错的选择;如果对用户体验要求不高,传统的Web应用架构也能满足需求。
-
可扩展性
:考虑到未来业务的增长和变化,RESTful架构的简单性和一致性使其更易于扩展;而SOAP和BPEL等技术在复杂场景下的扩展性可能会受到中间件和耦合度的限制。
12. 未来发展趋势
随着技术的不断发展,Web服务和REST架构也在不断演进。以下是一些可能的未来发展趋势:
-
融合发展
:不同技术之间可能会相互融合,取各自的优点,形成更强大的解决方案。例如,RESTful架构可能会借鉴SOAP的一些安全和事务处理机制,以提升其在复杂场景下的适用性。
-
轻量化和高效化
:未来的技术将更加注重轻量化和高效化,减少不必要的复杂性和资源消耗。例如,Ajax技术可能会进一步优化,解决可寻址性和无状态性的问题。
-
标准化和规范化
:随着Web服务的广泛应用,标准化和规范化将变得更加重要。相关的标准和规范将不断完善,以促进不同系统之间的互操作性和兼容性。
13. 实际应用案例分析
为了更好地理解上述技术在实际中的应用,下面通过一个具体案例进行分析。假设我们要开发一个在线旅游预订系统,该系统涉及用户信息管理、酒店预订、机票预订等多个业务模块。
- 架构选择 :考虑到系统的复杂性和对性能的要求,采用RESTful架构作为基础架构。RESTful架构的简单性和一致性可以提高开发效率,同时便于系统的扩展和维护。
- 安全实现 :对于用户信息和交易数据的安全,采用HTTPS协议进行传输加密,同时结合OAuth等认证机制,确保用户身份的合法性。
- 事务处理 :在酒店预订和机票预订等涉及资金交易的业务模块中,采用异步操作和补偿交易的方式,实现类似WS - BusinessActivity的事务处理机制,确保交易的一致性和可靠性。
- 用户体验 :使用Ajax技术实现异步请求和局部更新,提升用户在搜索、预订等操作时的交互体验,减少页面刷新带来的等待时间。
通过这个案例可以看出,在实际应用中,需要根据具体的业务需求和场景,综合运用不同的技术,以实现最佳的效果。
14. 技术实践建议
对于想要实践上述技术的开发者,以下是一些具体的建议:
-
学习基础知识
:首先要掌握Web服务、REST架构、JavaScript、XML等基础知识,了解它们的原理和应用场景。
-
实践项目
:通过实际项目来巩固所学知识,可以从简单的项目开始,逐步增加复杂度。例如,可以先开发一个简单的RESTful API,然后再结合Ajax技术实现一个Web应用。
-
参考开源项目
:参考优秀的开源项目,学习他人的设计思路和实现方法。例如,GitHub上有许多开源的RESTful API和Ajax应用,可以从中获取灵感。
-
持续学习
:技术在不断发展,需要持续学习和关注最新的技术动态,不断提升自己的技术水平。
15. 总结
本文详细介绍了Web服务和REST架构相关的多个重要概念,包括身份联合、可靠消息传递、事务处理、BPEL、ESB、SOA以及Ajax应用等。通过对这些概念的分析和对比,我们可以看到不同技术在不同场景下的优缺点。在实际应用中,需要根据具体的业务需求、安全要求、用户体验和可扩展性等因素,综合选择合适的技术。同时,随着技术的不断发展,我们也应该关注未来的发展趋势,不断学习和实践,以适应不断变化的技术环境。希望本文能够帮助读者更好地理解和应用这些技术,在实际开发中做出更明智的选择。
以下是一个mermaid格式的流程图,展示了Ajax应用的基本工作流程:
graph LR
A[用户请求主URI] --> B[服务器提供含脚本网页]
B --> C{用户操作触发脚本?}
C -- 是 --> D[脚本发起异步HTTP请求]
C -- 否 --> C
D --> E{请求完成?}
E -- 否 --> E
E -- 是 --> F[脚本解析响应数据]
F --> G[脚本修改用户视图]
通过这个流程图,可以更直观地理解Ajax应用的异步请求和局部更新机制。在实际开发中,开发者可以根据这个流程来实现具体的Ajax应用,提升用户体验。
超级会员免费看
3

被折叠的 条评论
为什么被折叠?



