RESTful Web Services 中文版
第 1 章 Programmable Web及其分类
1.传统网(human web)返回HTML文档。
2.可编程网(programmable web)返回XML文档。
Amazon S3的作用是“在桶(一种带标签的容器)里存放对象(一种带标签的数据)”。
Programmmable web是基于HTTP和XML技术的。
HTTP:信封里的文档
HTTP是一种基于文档的协议。客户端把文档放在信封里,然后发给服务器,作为回应,服务器把响应文档放在信封里,然后发给客户端。
HTTP响应可分为三个部分:
1.HTTP响应代码(HTTP response code)
2.相应报头(request headers)
3.实体主体(entity-body)或表示(representation)
XPath介绍
利用它,可以方便的将XML文档进行切分。关键的一点是:把XPath表达式看成一种从XML文档里提取标签(tag)或元素(elements)的规则。要知道一个XPath表达式是什么意思,只要从右边往左读就是了。
比如:表达式 //photo 的意思是:
寻找所有photo标签 photo
无论它在文档里何处 //
用REXML::XPath.each(doc,’//photo’)这行简单的Ruby代码可以实现遍历每一个photo标签,而不必遍历整个XML树。
相互竞争的服务架构
REST式、面向资源的架构
REST式架构意味着,方法信息(method info)都在HTTP方法里;面向资源的架构(ROA)意味着,作用域信息(scoping info)都在URI里。
RPC式架构
RPC架构意味着:方法信息和作用域信息都在信封或报头里,至于具体采用哪种信封,并不影响这里的分类。
XML-RPC是最典型的RPC架构的例子,虽然目前XML-RPC主要是一种遗留协议,但由于相对简单,并且容易理解。
REST-RPC混合架构
许多只读的Web服务,尽管他们起初也许是按RPC风格设计的,但都可以称得上是完全REST式和面向资源的。但是,如果该服务允许客户端修改数据的话,就会出现客户端所使用的HTTP方法与真正的方法信息不一致的情况–这样就不具有REST式服务的特征了。这样的服务,称之为REST-RPC服务。
Programmable Web涉及的技术
HTTP
URI:一个REST式面向资源的服务为客户端可能操作的每一则数据暴露一个URI;一个REST-RPC混合服务,为客户端可能进行的每一个操作暴露一个URI;一个RPC服务,为每个处理远程调用的进程暴露一个URI。
XML-RPC:是一种用于表达“函数调用及其返回值”的数据结构格式。专用于RPC式Web服务的。
SOAP:一种基于XML格式的信封格式。现在基于SOAP的服务,其SOAP信封里包含的是一个对RPC调用的描述。
WS-*:这些标准定义各种特定用途的SOAP报头。
WSDL(服务描述语言):用于描述SOAP Web服务的XML词汇。客户端通过参考WSDL文档,可以确切地知道它能够调用那些RPC式方法,这些方法需要哪些参数,以及返回值有什么数据类型。
WADL(Web应用描述语言):用于描述REST式Web服务的XML词汇。
第2章 编写Web服务客户端
Web服务就是网站
XPath讲解
以从右到左的顺序来读 /ResultSet/Result/Title/[]这个表达式,意思是:
寻找直接子节点 []
谁的直接子节点? Title标签的 Title/
哪里的Title标签?Result标签下的 Result/
那里的Result标签?ResultSet标签下的 ResultSet/
那里的ResultSet标签? 文档根目录的 /
用XML解析器处理响应
XML解析器可以分为三种:
有两种基本额你的XML解析策略,基于文档的(document-based)策略(如DOM等树式解析器)和基于事件的(event-based)策略(如SAX及”拖式“(pull)解析器)。
树式解析器把XML文档建模为一种嵌套的数据结构。DOM解析器是一种树式解析器,它实现了一个W3C定义的接口。
基于文档的树式策略,树式解析器可以把文档视为一个对象,最大的缺点是:必须把整个文档作为整体来处理。
SAX式或拖式解析器把一个XML文档转换成一个事件流,而不是数据结构。首标签(starting tag)、尾标签(closing tag)、XML注释(comments)、及实体声明(entity declaration)等都是事件(event)。假如几乎每个时间都要处理,那拖式解析器是很有用的。拖式解析器允许你一次处理一个事件。
SAX解析器更为复杂,但它在你只关心部分事件时会很有用。可以向SAX解析器注册一些回调方法,一旦定义好回调方法,解析器将按照既定的、跟文档无关的步骤执行下去:解析器将把XML文档转换为一系列事件,并接连处理文档中的每个事件;每当一个事件与回调方法所对应的事件相匹配时,解析器就会触发该回调方法,执行你定义的代码;在回调方法执行结束后,SAX解析器将接着继续处理事件序列。
基于文档的解析器的优点是:可以随机访问文档中的内容,而对于,基于事件的(event-based)解析器,事件触发之后,就没机会再次处理了。
Ruby: REXML
Ruby自带一个标准的XML解析器 — REXML。它同时支持DOM和SAX接口,而且对XPath的支持也很好。遗憾的是,REXML在严格性上处于一种尴尬的位置:它有一定严格性,因此不能指望用它来解析格式有错的XML;但它又没有严格到能拒绝所有格式有错的XML,所以又不能完全信任它的安全性。
如果想确保你只接受良构的XML,需要安装 libxml2库的Ruby语言映射。
如果希望处理格式有错的XML,最佳选择是 hpricot。以 hpricot gem的形式存在,速度比较快,接口比较直观且支持常见的XPath表达式。
第3章 REST式服务有什么特别从不同?
介绍Simple Storge Service
有两个流行的Web服务能满足这一目的,Atom发布协议(Atom Publishing Protocol, APP)和Amaze S3(Simple Storge Service)。
S3主要被用作两种用途:
1,备份服务器
2,数据寄存。
S3 的面向对象设计
S3基于2个概念:S3桶(bucket)和S3对象(object)。一个对象就是一则附有元数据的具名的(named)数据。一个桶(bucket)就是一个用于容纳对象(object)的具名的(named)容器。桶类似于硬盘上的文件系统,对象就类似于系统里的一个文件。桶只可以包含对象,不可以嵌套,只有通过给对象"directory/subdirectory/file-object"式的命名来模拟实现层次结构。
关于桶:
每个S3用户只能创建最多100个桶,而且你的桶不能跟其他用户的桶重名。建议要么把所有对象都放在一个桶里,要么用自己的项目名称或域名来给桶命名。
关于对象:
对象有四个相关部分:
1,对象所在桶的医用。
2,对象里的数据。术语称为value。
3,对象的名称,术语称为key。
4,与对象关联的一组元数据键-值对(metadata key-value pairs)。主要是自定义的元数据,不过也可以包含ContentType和Content-Dispostion等标准的HTTP报头的值。
假如把S3实现为一个面向对象代码库的话,那么将会有S3Bucker和S3Object这2个类,他们各自均有用于数据成员读写的方法。
资源
Amazon S3提供了2种Web服务,基于普通HTTP信封的REST式服务(RESTful service)和基于SOAP信封的RPC服务(RPC-style service)。
1,许多RPC式Web服务都是有它们内部的实现方法(implementation methods)自动生成的,他们暴露的服务接口跟它们在内部调用的编程语言接口是一样的。
2,REST方式暴露的是资源,而不是自己命名的函数。资源(resource)响应的不是像getObject这样自己命名的方法,而是响应GET,HEAD,POST,PUT,DELETE和OPTIONS这些标准的HTTP方法。
REST式S3服务提供三种资源,他们(及相应的URI)分别是:
桶列表(https://s3.amazonaws.com/):这种类型的资源只有一个;
一个特定的桶(https://s3.amazonaws.com/{name-of-bucket}):这种类型的资源最多有100个。
某个桶里的一个特定的S3对象(https://s3.amazonaws.com/{name-of-bucket}/{name-of-object}):这种类型的资源数量不限。
前面那个假想的面向对象的S3库里的每个方法,都可以转化为上述三种资源和六种标准方法的某种组合。如下:
每个资源都暴露相同的接口,并以同样的方式工作。如果要获取一个对象的值(value),就向该对象的URI发送GET请求,如果只要获取一个对象的元数据(metedata),就向该对象的URI发送HEAD请求,如果需要创建一个桶,就自己构造一个含有桶名的URI,然后向该URI发送PUT请求;如果要往一个桶里添加对象,就向含有桶名和对象名的URI发送PUT请求;如果要删除一个桶或对象,就向其URI发送DELETE请求。
HTTP响应代码
应该充分利用HTTP本身的响应代码。
如200 OK,404 Not Found,403 Forbidden, 400 Bad Request,409 Confilct。更多的请看 iceskysl的。
XPath讲解
以从右到左的顺序来读 //Bucket/Name这个XPath表达式,它的意思是:
寻找所有Name标签 Name
那里的Name标签? 直接在Bucket标签下的 Bucket/
那里的Bucket标签?任何地方的 //
第4章 面向资源的架构
一个具体的REST式架构 — 面向资源的架构(Resource-Oriented-Architecture, ROA)。ROA是一种把实际问题转换成REST式Web服务的方法:她令URI、HTTP和XML具有跟其他Web应用一样的工作方式,令程序员们容易使用它们。
将介绍面向资源的架构的功能成分:资源、资源名称、资源表示、资源间的链接。
可寻址性 Addressability
可寻址的应用可以为每一则信息都提供一个URI。可以将URI发给其他人,缓存或被加入书签。
无状态性 Statelessness
意味着每个HTTP请求都是完全孤立的。
表示 Representations
表示的选择 为一个资源的多个表示添加不同的URL。
链接与连通性 links and Connectedness
统一接口 The Uniform Interface
POST
PUT和POST的区别在于:假如是客户端负责决定新资源采用什么URI,就是用PUT;假如是服务器负责新资源采用什么URI,就用POST。
在REST设计中,通常被用于创建从属资源。
第5章 设计只读的面向资源的服务
资源设计
面向对象程序的标准设计方法是把系统分解成一个个功能部件,即其中的名词。RPC式架构的设计方法是把系统分解成一个个动作,即其中的动词。
根据需求创建只读资源
1,规划数据集
2,把数据集划分为资源
对于其中的每种资源:
3,用URI为该资源命名
4,设计发给客户端的表示
5,用超链接和表单把该资源与已有资源联系起来
6,考虑有哪些典型的事件经过
7,考虑可能出现哪些错误情况
命名资源
URI设计有三条基本原则:
1,用路径变量(path variables)来表示层次结构(hierarchy)。
2,在路径变量里加上标点符号,以消除误解。当作用域信息的次序有关紧要时,就用逗号。否则就用分号。
3,用查询变量(query variables)来表示算法的输入。
第6章 设计可读写的面向资源的服务
针对上一节的步骤:
添加了2个补充:
4,暴露一个统一接口的子集
5,设计来自客户端的表示
第 7 章 一个服务实现
第11章 将Ajax应用作为REST客户端
Ajax的正式定义:Ajax应用就是在web浏览器里运行的Web服务客户端。
Ajax应用的不利方面在于,每个应用状态都是同一个URI — 即最终用户访问的第一个URI。这破坏了可寻址性(addressability)和无状态性(statelessness)。虽然幕后的web服务也许是可寻址的和无状态的,但最终用户无法把一个特殊状态加入收藏夹,而且浏览器的后退按钮也失去了原有的作用。
利用 XMLHttpRequest 将XML文档解析成DOM对象,并通过 request.responseXML 来访问该DOM对象。
如何发送请求:
1, 构造一个XMLHttpRequest对象。
例如在Mozilla系列浏览器(例如FF)里,应该是这样的: Request = new XMLHttpRequest();
2,调用XMLHttpRequest.open方法,并传入跟请求有关的信息,其中前2个参数是可选的:
request.open([HTTP method], [URI], true, [Basic auth username], [Basic auth password] );
其中第三个参数是用来控制浏览器进行异步操作的。
如果选择异步操作,必须设置一个回调函数:
request.onReadyStateChange = [Name of handler function];
还可以用 setRequestHeader 方法为请求设置 HTTP 请求报头:
request.setRequestHeader([Header name], [Header value]);
最后,通过调用 send 方法向 HTTP 服务器发送请求, 如果是 POST 或 PUT 请求,就应当把实体主体作为参数提交给 send 方法。如果是其他请求,就该该参数置为 null。
request.send([Entity-body]);
如果一切顺利的话,你的回调函数将被调用4次,而且每一次 request.readeyState 都有不同的值。当值等于4时,表明请求已经完成。可以处理响应了。
处理响应:
请求完成时,浏览器会最后一次调用你的回调函数。这时,有以下属性可供 XMLHttpRequest 实例读取。
State属性是数字状态代码。
responseXML 属性里是一个由解析响应文档而得到的 DOM 对象。要求服务器返回XML媒体类型。
responseText 属性里是响应文档的原始文本串。当服务器返回JSON或非XML格式的响应文档时,这个属性比较有用。
可以用getResponseHeader拿到响应包头的值。
Ajax应用可以通过重新实现“后退”与“前进”按钮,来挽回失去的无状态性。但不一定要缺乏创意地模仿浏览器的行为。
跨浏览器问题和Ajax库:
针对不同的浏览器,实例化 XMLHttpRequest的方法是不同的。
以下是Bret Taylor编写的 JavaScript 函数。总是创建一个具有 XMLHttpRequest 功能的对象。 可以在他的网站(http://ajaxcookbook.org)上找到该函数。
function createXMLHttpRequest() {
if ( typeof XMLHttpRequest != "undefined") { // typeof 运算符返回一个用来表示表达式的数据类型的字符串。
return new XMLHttpRequest();
} else if (typeof ActiveRequest != "undefined") {
return new ActiveXObject("Microsoft.XMLHTTP");
} else {
throw new Error("XMLHttpRequest not supported");
}
}
相应的构造方法也该改为 Request = createXMLHttpRequest();
还有2个重要的跨浏览器问题。
首先,Safari浏览器不支持 PUT 和 DELETE方法。如果需要 Safari 能够访问你的服务,就得允许它们用重载的 POST 来模拟 PUT 和 DELETE 请求。
另外,IE会把成功获得的相应长期缓存下来。这样即使资源已经变化,用户也看不到资源的变化。规避此问题的最好方法是:服务器在返回表示时,同时附上适当的 ETag 响应报头,或者干脆用 Cache-Control 报头来禁用缓存。
颠覆浏览器安全模型:
Web浏览器遵循这样一条基本规则,即防止来自一个域名的代码向另一个域名发送HTTP请求。可以采用2个方式来绕过此限制:请求代理和JoD。
请求代理:
客户端向 example.com发送请求,在服务端向 yahoo.com 发送那些请求。
Yahoo 文档 ”Proxy for Cross-Domain XMLHttpRequest Calls“详细讲述了这一技术。 在这个技术中,在服务器预留一部分URI空间,用于模拟另一个服务URI空间。当收到一个对应该URI空间的请求时,把该请求转发给那个外部服务器,然后把外部服务器发送你的响应返回给客户端。
JavaScript on Demand:
JoD技术的基本想法是,HTML的 script 标签不一定非得包含写死的JavaScript代码。它可以通过 src 属性来引用位于另一个 URI 的代码。当 Web 浏览器遇到一个 script 标签时,它会加载 src 属性指定的外部脚本,并执行其中的代码。
正常的Web服务客户端可以在调用Web服务后,先查看它返回的表示,如果没有问题再对它进行处理。但假如是客户端提供可执行代码,而Web服务通过特殊手段来自动运行它的话,你就等于对它毫不设防了。
JoD只能发送GET请求。
用 src 属性来引用一个对象,要比用它来获取定制生成的 JavaScript 安全得多。 script 并非唯一可用于让浏览器加载表示的HTML标签。 img 和 frame 标签也可以。
第12章 REST式服务框架
REST 并不是一种架构,而是一种评判架构的方式。面向资源的架构是一种架构,它对设计加以约束,是你能够很容易地把问题划分为一个个REST式资源。
RoR的成功主要在于它的简单化假设。并不是提供了一大堆用以解决问题的工具,只是为你提供了一种用以解决各类常见问题的方式。
资源、控制器和视图
每个Rails控制器可以暴露两种资源。你可以有一个”列表“或”工厂“资源(响应GET和POST请求)和许多”对象“资源(响应GET、PUT和DELETE请求)。
返回的表示
Rails可以根据客户端请求的目标URI或Accept报头决定返回哪个表示。
收到的表示
Rails的工作就是根据收到的表示生成一组关键字-值对,并以params hash 的形式来提供这些关键字-值对。
Rails/ROA 设计步骤
1,规划数据集
2,把数据集分配给一个个控制器
对于每一个控制器:
a,该控制器暴露的是一个列表或工厂资源吗?
b,该控制器暴露的是一组对象资源吗?
c,该控制器暴露的是一个用于创建或修改资源的表单资源吗?
对于列表和对象资源:
设计来自客户端的表示。
设计返回给客户端的表示。
把该资源与已有资源联系起来。
考虑有哪些典型的事件经过?
考虑可能出现哪些错误情况? 同样,这里常常可以采用基于数据库技术的应用的控制流。
Restlet的思想是 HTTP 客户端与 HTTP 服务端之间的差别,对于架构来说无所谓。一个软件应可以既充当Web客户端又充当Web服务端。
需要注意2点:
1,Web服务器的实际工作是由一个非常紧凑的、基于Simple框架的HTTP服务器连接器来处理的;
2,用强大的db4o 对象数据库来存储领域对象的。
Django的思想是:URI是一个Web应用的用户界面的一个重要部分,不应是自动生成的。Django让你从零开始设计URI。
Django框架的独特风格是:总在URI的末尾加一个斜杠。
urls.py也是采用URI模型的出现次序来依次比较的。和Rails不同的是,Django不用为我判断URI格式。
作为Django视图资源的实现
在Rails中,为了实现一个资源在统一接口幕后的行为,需要把代码放在controller中。 而在Django中,这些代码是放在view里的。
inove.