所以到现在为止我已经明白我们都应该实现我们的RESTful服务,提供表示,使客户能够遵循HATEOAS原则.虽然理论上这一切都很有道理,但我一直在网上搜索一些严格遵循这个想法的客户端代码的好例子.
我读的越多,我就越开始觉得这是一次学术讨论,因为没有人真正这样做!人们可以呻吟所有他们喜欢的WS-*堆栈的许多缺陷但至少很清楚如何编写客户端:你可以解析WSDL并生成代码.
现在我明白这对于一个好的RESTful服务来说不是必需的:你应该只需要知道所涉及的关系和表示,你应该能够动态地对这些做出反应.但即使如此,现在这个原则是不是应该被提炼出来并抽象成一些常见的库?提供有关您可能收到的表示和关系的信息,并获得可在您的应用程序中使用的更有用的更高级代码?
这些只是我的半生不熟的想法,但我只是担心如果我现在潜入并编写一个正确的RESTful API,实际上没有人能够使用它!或者至少使用它会在背后造成这样的痛苦,因为人们将不得不编写胶水代码来解释我提供的关系和表示.
任何人都可以从客户的角度阐明这一点吗?有人可以展示一个正确的动态/反应式RESTful客户端代码示例,这样我就可以了解我实际上正在为之写作的受众吗?(更好的是提供一些抽象的客户端API的一个例子)否则它的所有理论......
[编辑:请注意,我在这里发现了一个类似的问题,我认为这个问题没有得到回答,作者被一个维基百科的存根所取代!]
在我们目前的项目中,我们已经完成了一半.我们返回的表示是从域对象生成的,客户端可以使用XML,JSON或XHTML来请求它们.如果它是像Firefox这样的XHTML客户端,那么一个人可以从众所周知的根资源中看到一组出站链接,并可以浏览所有其他资源.到目前为止,纯HATEOAS,和开发人员的一个伟大的工具.
但是当客户端是程序而不是使用浏览器的人时,我们会关注性能.对于我们的XML和JSON表示,我们目前已经抑制了相关链接的生成,因为它们使表示大小增加了三倍,从而大大影响了序列化/反序列化,内存使用和带宽.我们的另一个效率问题是,对于纯HATEOAS,客户端程序在从众所周知的链接浏览到所需信息时会产生数倍的HTTP请求.因此,从效率的角度来看,如果客户知道其中编码的链接,那么这似乎是最好的.
但这样做意味着客户端必须执行大量的字符串连接才能形成URI,这很容易出错并且很难重新排列资源名称空间.因此,我们使用模板系统,其中客户端代码选择模板并要求它从参数对象扩展自己.这是一种形式填充.
我真的很想看到别人在这方面遇到了什么.除了性能方面,HATEOAS似乎是一个好主意.
编辑:我们的模板是我们在Restlet框架之上编写的Java客户端库的一部分.客户端库处理HTTP请求/响应,HTTP标头,反序列化/序列化,GZIP编码等的所有细节.这使得实际的客户端代码非常简洁,并有助于将其与服务器端的某些更改隔离开来.
罗伊菲尔丁关于HATEOAS的博客文章有一个非常相关和有趣的讨论.
到目前为止,我已经构建了两个访问REST服务的客户端.两者都只使用HATEOAS.我已经取得了巨大的成功,无需更新客户端即可更新服务器功能.
我使用xml:base来启用相对URL以减少我的xml文档中的噪音.除了加载图像和其他静态数据之外,我通常只关注用户请求的链接,因此链接的性能开销对我来说并不重要.
在客户端上,我认为需要创建的唯一常见功能是围绕我的媒体类型的包装器和管理链接的类.
更新:
从客户端的角度来看,似乎有两种不同的方式来处理REST接口.第一个是客户端知道它想要获取什么信息的地方,并且知道它需要遍历以获取该信息的链接.当客户端应用程序的人类用户控制要遵循哪些链接并且客户端可能事先不知道将从服务器返回什么媒体类型时,第二种方法是有用的.对于娱乐价值,我分别称这两种类型的客户端,数据挖掘者和调度员.
例如,想象一下Twitter API实际上是RESTful,我想写一个客户端,可以检索特定Twitter用户的最新关注者的最新状态消息.
假设我使用了很棒的新的Microsoft.Http.HttpClient库,并且我编写了一些"ReadAs"扩展方法来解析来自twitter API的XML,我想它会是这样的:
var twitterService = HttpClient.Get("http://api.twitter.com").Content.ReadAsTwitterService(); var userLink = twitterService.GetUserLink("DarrelMiller"); var userPage = HttpClient.Get(userLink).Content.ReadAsTwitterUserPage(); var followersLink = userPage.GetFollowersLink(); var followersPage = HttpClient.Get(followersLink).Content.ReadAsFollowersPage(); var followerUserName = followersPage.FirstFollower.UserName; var followerUserLink = twitterService.GetUserLink(followerUserName); var followerUserPage = HttpClient.Get(followerUserLink).Content.ReadAsTwitterUserPage(); var followerStatuses = HttpClient.Get(followerUserPage.GetStatusesLink()).Content.ReadAsTwitterUserPage(); var statusMessage = followerStatuses.LastMessage;
为了更好地说明这个例子,想象一下你实现了一个提供家谱信息的客户端.客户端需要能够显示树,深入了解特定人员的信息并查看相关图像.请考虑以下代码段:
void ProcessResponse(HttpResponseMessage response) { IResponseController controller; switch(response.Content.ContentType) { case "vnd.MyCompany.FamilyTree+xml": controller = new FamilyTreeController(response); controller.Execute(); break; case "vnd.MyCompany.PersonProfile+xml": controller = new PersonProfileController(response); controller.Execute(); break; case "image/jpeg": controller = new ImageController(response); controller.Execute(); break; } }
客户端应用程序可以使用完全通用的机制来跟踪链接并将响应传递给此调度方法.从这里开始,switch语句将控制权传递给特定的控制器类,该控制器类知道如何根据媒体类型解释和呈现信息.
显然,客户端应用程序还有很多部分,但这些部分与HATEOAS相对应.随着我浏览了许多细节,请随意让我澄清任何要点.