超媒体到底是什么?!
如果你有幸听说过REST架构风格,那么你也可能听说某些人认为统一接口是最重要的约束,尤其是该接口在制约资源上能被调用的方法方面。但是,你没有意识到的是,对于统一接口还有很多东西。特别是一个被笨拙地冠以“超媒体即应用状态引擎(hypermedia as the engine of application state)”名字的子约束,它可以认为是REST最重要的约束,在某种意义上它独力塑造了我们所熟知的RESTful系统的大部分的“形状”。
定义
不幸的是,REST论文没有在这个约束上展开,除了它的名字和对它实际应用的描述:
所以,模型应用是一个从一个状态迁移到下一个状态的引擎,迁移是通过对当前表述性集合中可选状态进行检查和选择而完成的。
尽管它给我们提供了一个有用的描述,但是在我看来,它不能帮助我们真正理解约束本身的作用域;约束真正允许的是什么,不允许的又是什么。由此开始,接下来还值得注意的是我们能从约束自身名字中获取些什么信息。
“应用状态”指的是一个状态,它决定了用户在完成一个任务的流程中所处的“位置”。例如,在做个人银行业务的时候,用户是正在浏览帐户余额,还是在填写账目付款单,或者是在订购新的支票?它们每个都是不同的应用状态。有些人错误地认为“状态”是指资源状态,在上面例子里,资源是指帐户余额或者近期付款清单。但“应用状态”和“资源状态”是不同的。
应用状态也被称为“会话状态”,该状态也是REST的“无状态”约束所指的状态,这种约束要求客户端独自维持状态。反之,如果你使用诸如VNC或者Windows远程桌面的远程会话技术,那么应用状态完全保存在服务器上。
Ted Nelson在1962年创造了“超媒体”一词,是他发明的“超文本”泛化。鉴于超文本产生了内部互联的文本文档,那么超媒体将范围扩展到了任何形式的媒体。当然,两者的关键点都是我们可以在使用的内容嵌入链接。
约束实战
REST在2003/2004年开始获得一些从事面向互联网服务开发者的关注——至少这些开发者确实将他们的服务冠以“REST”的绰号——最明显的是两个高调、自我标榜的“REST API”:Flickr和Amazon。有趣的是两个服务也同时提供了基于SOAP的接口,但是与“REST API”相比,这两个SOAP API都没有显出更多的使用。最终,REST社区拥抱了这些服务,并且将它们作为一种进一步解释在Web上使用REST风格的价值和魅力的工具。不幸的是,这些API存在一个问题:它们不完全是RESTful的,因为它们无视(至少)一个REST约束。事实上,Flickr API(以及Amazon、del.icio.us等等)的问题比我们在此讨论的要多得多,此处我们只关注那些和超媒体有关系的问题。
幸运的是,我们不需要为寻找这些问题花太多时间。以“flickr.contacts.getList”操作返回的样本数据为例,用户可使用该操作获得他们自己的联系人清单:
<contacts page="1" pages="1" perpage="1000" total="3">
<contact nsid="12037949629@N01" username="Eric" iconserver="1"
realname="Eric Costello"
friend="1" family="0" ignored="1" />
<contact nsid="12037949631@N01" username="neb" iconserver="1"
realname="Ben Cerveny"
friend="0" family="0" ignored="0" />
<contact nsid="41578656547@N01" username="cal_abc" iconserver="1"
realname="Cal Henderson"
friend="1" family="1" ignored="0" />
</contacts>
这里,“nsid”属性包含了一个表示单个联系人的唯一标识符,在这个例子里是其中的三个联系人。但是一旦客户端已经检索到了这个文档,接下来会怎样?如果他们想了解更多关于Cal Henderson的信息怎么办?通过快速检查Flickr API文档,可以发现有一个叫做"flickr.people.getInfo"的操作,它接收nsid作为一个参数,返回那个nsid字符串所标识联系人的更多信息。那么为了获得关于Cal的更多信息,我们在HTTP GET消息中需要使用的URI将是:
http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=41578656547@N01这不是超媒体。一个超媒体解决方案会使用标准化的标识符——对于Web来说就是URI——而非私有的标识符,这避免了客户端在从联系人清单文档浏览到个人信息文档过程中需要特定于Flickr的知识。如果采用标准标识符,那么第一个文档应该是:<contacts page="1" pages="1" perpage="1000" total="3">
<contact nsid="http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=12037949629@N01" username="Eric" iconserver="1"
realname="Eric Costello"
friend="1" family="0" ignored="1" />
<contact nsid="http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=12037949631@N01" username="neb" iconserver="1"
realname="Ben Cerveny"
friend="0" family="0" ignored="0" />
<contact nsid="http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=41578656547@N01" username="cal_abc" iconserver="1"
realname="Cal Henderson"
friend="1" family="1" ignored="0" />
</contacts>