表现良好的RESTful客户端交互

时间:2009-08-04 04:12:57

标签: django rest

关于实现严格遵守REST架构原则的数据访问客户端,我有一个相当简单的问题。首先,让我们假设我有一个行为良好的REST API,我想使用Django应用程序。我将首先了解可用的服务 (已编辑后续行动)

GET example.com/services/ HTTP/1.1

HTTP/1.1 200 OK
<?xml version="1.0" encoding="UTF-8"?>
<services>
  <service>
    <name>Widgets</name>
    <link>http://example.com/services/widgets/</link>
    <item_link>http://example.com/services/widgets/{widget_id}/</item_link>
  </service>
  <service>
    <name>Factories</name>
    <link>http://example.com/services/factories/</link>
    <item_link>http://example.com/services/factories/{factory_id}/</item_link>
  </service>
  ...
</services>

现在,由于我正在构建一个基于使用此API的Django应用程序,我将如何继续继续探索这些服务?要遵守REST原则,我的应用程序必须由收到的超媒体驱动。我想第一步很容易 - 通过给出的名称与服务进行交互。我按如下方式设置了Django视图:

def get_service(request, service_name):
    doc = etree.parse(urllib.urlopen('http://example.com/services/'))
    uri = doc.xpath("service/name[.='%s']/following-sibling::*" % service_name)[0].text
    ...

我将从中执行另一项请求 (已进行后续编辑)

GET example.com/services/widgets/ HTTP/1.1

HTTP/1.1 200 OK
<?xml version="1.0" encoding="UTF-8"?>
<widgets>
  <item_link>http://example.com/services/widgets/{widget_id}/</item_link>
  <widget>
    <id>1</id>
    <name>Whizbang Foobar</name>
    <link>http://example.com/services/widgets/1</link>
  </widget>
  ...
</widgets>

现在,我将在渲染的Django模板中显示一个简单的小部件列表。但是,从这里开始,如何继续与REST服务进行交互?也许我已经迷惑自己陷入混乱,但我能想到的唯一合理的东西是实现了大量的应用程序视图或瘦Django数据模型来持久化服务URI。

我主要担心的是,如果不严格遵守REST架构指南,这是微不足道的,但我觉得我完全错过了试图这样做的船。我理解设计合适的REST API和客户端并不“简单”,但似乎我迫切需要一个类似的例子来完成实际的实现。

我为这个问题的长度和冗长以及对于那些干瘪的读者不可避免的面子表示道歉。

随访:

以下是实现这些交互的有效方式(使用URI模板)吗?出于演示目的(代替更抽象的实现),另一个用于检索资源集合项的Django视图:

def get_item(request, service_name, item_id):
    doc = etree.parse(urllib.urlopen('http://example.com/services/'))
    uri = doc.xpath("service/name[.='%s']/following-sibling::item_link" % service_name)[0].text
    ...

然后是后续请求:

GET example.com/services/widgets/1 HTTP/1.1

HTTP/1.1 200 OK
<?xml version="1.0" encoding="UTF-8"?>
<widget>
  <id>1</id>
  <name>Whizbang Foobar</name>
  <tags>foo bar baz ham eggs</tags>
  <index_link>http://example.com/services/widgets/</index_link>
</widget>

4 个答案:

答案 0 :(得分:3)

  

我主要担心的是,如果不严格遵守REST架构指南,这是微不足道的,但我觉得我完全错过了试图这样做的船。我理解设计合适的REST API和客户端并不“简单”,但似乎我迫切需要一个类似的例子来完成实际的实现。

我能找到的最好的例子是Sun Cloud API。大多数文档描述了系统使用的各种媒体类型,这似乎是解决这类问题的关键。

我发现在开发API的同时编写客户端很有帮助。这样你就可以发现什么可能使你的API很难立即编码并解决问题。

这并不容易。如果您遵循HATEOAS约束到其逻辑结论,您定义的每种媒体类型将由一个客户系列之一处理。在某种程度上,您可以使所有资源都遵循类似的行为模式,您编写客户的工作将变得更加容易。

例如,您可以定义仅列出相关资源的媒体类型“索引”。索引定义分页链接,获取列表中的项目,按名称查找项目等。

然后,您可以定义一个名为“Item”的基本媒体类型。项目有一个链接,用于显示其父索引,更新/删除自身等。您的资源窗口小部件可以由两种不同的媒体类型表示 - 一个索引和一个基于项目。

您可以从实现处理Index媒体类型的单个类开始。然后,您可以编写一个基类来处理所有常见的Item媒体类型行为。最后,您可以编写一个Widget客户端来处理所有特定于窗口小部件的行为,并扩展Item客户端。这些客户可以用他们所写语言的惯用方式公开他们的功能(更多链接和数据字段的可用性)。

处理来自服务器的响应将是将响应的mime类型与您编写的客户端之一进行匹配。

换句话说,即使整个服务的客户端由许多范围有限的客户组成,它们也会基于共同的行为,因此可能会以干燥的方式实施。

答案 1 :(得分:3)

根据我的经验,如果表示及其包含的链接直接转换为客户端UI,则REST模型更有意义。在这种情况下,用户可以指导探索REST接口。

我常常看到有人试图将REST接口用作一种基于HTTP的数据访问层。使用此心智模型,超链接数据仅提供结构数据关系。在不违反RESTful约束的情况下,在该接口之上构建应用程序行为变得很困难。

我喜欢将RESTful接口视为向应用程序提供UI内容,该应用程序将使用某种任意技术呈现该内容。遗憾的是,REST经常与Web服务进行比较,在我看来,Web服务适用于不同的架构层。 Web服务提供将由客户端应用程序处理的数据。 RESTful接口应该提供将呈现给用户的内容。

当然,您可以使用REST接口将数据传递到远程应用程序,但将其视为简化的屏幕抓取。

在编写REST接口的客户端时,我发现编写一个自定义Web浏览器非常有用,该浏览器只能理解该RESTful接口提供的媒体类型,并且硬编码为从特定URL开始,没有地址栏!

答案 2 :(得分:1)

如果我理解你的问题,你想探索一个未知的服务,对吗?

如果是这样,那么你可以继续对“widget”资源发出OPTIONS请求,看看它支持哪些HTTP方法(这些方法应该列在Allow标题中。响应)。
然后,您可以对<link rel="whatever">元素中的所有URI执行相同操作。如果以这种方式找到的资源表明它支持GET,则获取它并重复...
这样您就可以浏览所有嵌套资源。

这种探索当然只会让你到目前为止,因为真的与服务进行交互,你需要知道它的媒体类型(或表示),以及不同的{您发现的{1}}行动实际上意味着。此步骤无法自动执行,您必须阅读该服务的文档并相应地构建您的客户端。我建议阅读文章“How to GET a Cup of Coffee”,我认为这很好地解释了这种互动。

答案 3 :(得分:1)

Sun Cloud API文档是RESTful API的一个很好的例子,侧重于媒体类型。