Python对象层次结构与REST资源?

1 投票
2 回答
1993 浏览
提问于 2025-04-17 03:26

我现在在我的Python应用程序(使用Twisted)中遇到了一个“架构”问题,这个应用程序使用了REST API,我想听听大家的意见。

警告!接下来会有一段比较长的内容!

假设我们有以下的对象层次结构:

class Device(object):
  def __init__():
    self._driver=Driver()
    self._status=Status()
    self._tasks=TaskManager()

  def __getattr__(self, attr_name):
    if hasattr(self._tasks, attr_name):
        return getattr(self._tasks, attr_name)
    else:
        raise AttributeError(attr_name)

 class Driver(object):
   def __init__(self):
     self._status=DriverStatus()

   def connect(self):
      """some code here""" 

   def disconnect(self):
     """some code here"""

 class DriverStatus(object):
   def __init__(self):
     self._isConnected=False
     self._isPluggedIn=False

我还有一个相当复杂的对象层次结构(上面的元素只是其中的一部分)。所以,现在在REST API中,我得到了以下资源(我知道,REST并不是关于URL层次结构的,而是关于媒体类型的,但为了简单起见,我这样说):

/rest/environments

/rest/environments/{id}

/rest/environments/{id}/devices/

/rest/environments/{id}/devices/{deviceId}

/rest/environments/{id}/devices/{deviceId}/driver

/rest/environments/{id}/devices/{deviceId}/driver/driverstatus

几个月前,我从一个“脏”的SOAP类型API切换到了REST,但我开始对如何处理似乎增加的复杂性感到不确定:

  1. REST资源/媒体类型的增加:例如,现在我不仅仅有一个设备资源,还有这些资源:

    • 设备
    • 设备状态
    • 驱动程序
    • 驱动程序状态

    虽然从RESTful的角度来看这些都是合理的,但有很多子资源,每个都映射到一个单独的Python类,这正常吗?

  2. 将方法丰富的应用核心映射到RESTful API:在REST中,资源应该是名词,而不是动词:有没有好的规则或建议,可以从一组方法智能地定义一组资源?(我找到的最全面的例子似乎是这篇文章

  3. API逻辑影响应用结构:应用程序的API逻辑是否至少部分上应该指导其内部逻辑,还是将关注点分离是好做法?也就是说,我是否应该有一个中间层的“资源”对象,负责与应用核心进行通信,但与核心的类一一对应?

  4. 如何以RESTful的方式正确处理以下情况:我需要能够在客户端显示可用的驱动程序类型列表(即类名,而不是驱动程序实例):这是否意味着要创建另一个资源,比如“DriverTypes”?

这些问题比较复杂,所以感谢你的耐心,任何指点、反馈和批评都非常欢迎!


对S.Lott:

  • 我所说的“资源过于分散”是指很多不同的子资源,基本上仍然适用于同一个服务器端实体。

  • 关于“连接”:那会是“驱动程序状态”资源的一个修改版本吗?我认为连接是始终存在的,因此使用“PUT”,但考虑到“PUT”应该是幂等的,这样做会不会不好?

  • 你说得对,“停止编码并重新思考”,这就是我问这些问题并把事情写下来,以便更好地了解的原因。

    -问题是,现在基本的“现实世界对象”,正如你所说的,作为REST资源/资源集合对我来说是有意义的,并且它们通过POST、GET、UPDATE、DELETE被正确操作,但我很难理解REST方法对于那些我本能上不认为是“资源”的东西。

2 个回答

0

你可以用一个 Site 对象来表示路径部分 /rest,但是路径中的 environments 必须是一个 Resource。接下来,你需要在 environmentsrender_* 方法中自己处理层级关系。你会得到一个 request 对象,它有一个 postpath 属性,里面包含了路径的其余部分(也就是在 /rest/environments 之后的部分)。你需要从中解析出 id,判断路径中是否包含 devices,如果有的话,就把剩下的路径(和请求)传递给你的设备集合。很遗憾,Twisted 不会为你处理这个决定。

4

规则一:REST是关于对象的,而不是方法。

REST的“资源”变得太分散了。

错!永远都是错的。REST资源是独立的,不能说它们“太”分散了。

现在我不再只有一个设备资源,而是有这些资源:

设备 设备状态 驱动程序 驱动程序状态

虽然从[RESTful]的角度来看这些都很合理,但有这么多子资源,每个都对应一个单独的Python类,这正常吗?

其实,这些并不合理,所以你才会问这个问题。

设备就是一个东西。比如说:/rest/environments/{id}/devices/{deviceId}

它有状态。你应该考虑把状态和设备信息一起提供,作为一个描述设备的综合文档。

仅仅因为你的关系数据库是规范化的,并不意味着你的RESTful对象也需要和数据库一样严格规范化。虽然这样做更简单(而且很多框架让这变得非常简单),但这可能并不有意义

考虑到连接总是存在,因此使用“PUT”,但考虑到“PUT”应该是幂等的,这样做会不会有问题?

连接并不是总是存在的,它们可能会时有时无。

虽然关系数据库可能有一个多对多的关联表,你可以对其进行更新,但这是一种特殊情况,在数据库管理员的世界之外并没有太大意义。

两个RESTful事物之间的连接很少是一个单独的东西。它是每个RESTful事物的一个属性。

这个“连接”到底是什么并不清楚。你只是模糊地提到它,但没有提供细节。

缺乏任何可用的事实,我猜测你是在连接设备和驱动程序,并且有某种[Device]<-[Driver Status]->[Driver]的关系。设备和驱动程序之间的连接可以是一个单独的RESTful资源。

它也可以很容易地是设备或驱动程序的一个属性,而不需要一个单独、可见的RESTful资源。

[再说一次,一些框架像Django-Piston让暴露底层类变得很简单,但这并不总是合适的。]

有没有好的规则/建议来智能地从一组方法定义一组资源?

有。不要这样做。资源不是方法。基本上就是这样。

如果你有很多方法——除了CRUD之外——那么你可能在数据模型上有问题。你可能在关系模型中表达的事物类别太少,而状态更新的事物太多。

有状态的对象并不本质上是坏事,但需要认真审视。在某些情况下,改变对象状态的PUT请求可能应该是一个POST请求,用于添加对象的历史记录。“当前”状态是最后一个POST的内容。

还有。

你不必把每个资源都简单化为一类事物。你可以有资源是集合。你可以向一个综合(实际上是一个外观)“资源”POST一个相当复杂的文档。这个复杂的文档可以在数据库中隐含多个CRUD操作。

你正在偏离简单的RESTful。你的问题仍然故意模糊。“方法丰富的应用核心”并没有太大意义。没有具体的例子,很难想象。

API逻辑影响应用结构

如果这些是不同的,你可能在制造不必要的、没有价值的复杂性。

应用分离关注点是好习惯吗?

当然。为什么要问?

很多问题似乎源于我对如何将一个方法丰富的API映射到RESTful API的困惑,资源应该是名词,而不是动词:那么,什么时候考虑一个元素是REST“资源”是明智的?

资源是由你的问题领域定义的。通常是一些具体的东西。方法(在“方法丰富的API”中)通常无关紧要。它们是CRUD(创建、检索、更新、删除)操作。如果你有一些不是本质上CRUD的东西,你就得停止编码。停止写代码,重新思考API,使其更像CRUD。

CRUD - 创建-检索-更新-删除对应REST的POST-GET-PUT-DELETE。如果你无法将你的问题领域重新表述为这些术语,就停止编码。停止编码,直到你理解CRUD规则。

我需要能够在客户端显示可用的驱动程序类型列表(即类名,而不是驱动程序实例):这是否意味着要创建另一个资源,比如“DriverTypes”?

没错。它们已经是你问题领域的一部分。你已经定义了这个类。你只是通过REST将其公开。


关键在于,问题领域有现实世界的对象。你有类的定义。它们是具体的东西。REST传递这些具体事物的状态。

你的软件可能有一些无形的东西,比如“关联”或“链接”或“连接”,这些都是软件解决方案的一部分。这些无关紧要。它们是实现细节,而不是现实世界的事物。

一个“关联”总是可以从两个现实世界的RESTful资源中看到。一个资源可能有一个外键引用,允许客户端进行RESTful获取另一个相关对象。或者一个资源可能有一组其他相关对象,单个GET请求可以检索一个对象和一组相关对象。

无论如何,现实世界的RESTful资源是可用的。关系只是隐含的。即使它是一个物理的多对多数据库表——这并不意味着它必须被公开。[再说一次,一些框架让暴露一切变得非常简单,但这并不总是好事。]

撰写回答