7.4. 资源

7.4.1. 资源

资源就是支持 REST 的网站服务的数据资源。每个资源类型包括一组通用的参数,REST API 使用这些参数构成一个 XML 或 JSON 资源表述(resource representation)。用户可以查看资源的表述,然后编辑参数后使用 API 把表述发送到资源的 URL。用户也可以使用 REST 删除资源。
支持 REST 的网站服务也会把资源组合成集合(collections)。用户可以查看一个集合中的所有资源的表述。用户也可以向特定的集合发送资源表述来在相应的集合中创建一个新资源。

7.4.2. 获取一个资源

使用从一个集合列表获得的 URI 的 GET 请求来得到一个资源状态列表。
包括一个 Accept HTTP 头来定义响应格式的 MIME 类型。
GET /api/[collection]/[resource_id] HTTP/1.1
Accept: [MIME type]
对于一些资源,可以使用带有 All-Content: true 的头来获取它们的额外信息。RESTful Service Description Language 介绍了哪些链接支持这个头。
GET /api/[collection]/[resource_id] HTTP/1.1
Accept: [MIME type]
All-Content: true

7.4.3. 更新一个资源

使用一个 PUT 请求修改资源的属性,这个请求需要包括一个从前一个对资源 URI 的 GET 请求所获得的更新描述。请参阅相关资源类型的文档来了解更多关于更新资源的信息。
POST 请求需要一个 Content-Type 头。它会告诉 API 在内容数据中使用的表述 MIME 类型。
包括一个 Accept HTTP 头来定义响应格式的 MIME 类型。
PUT /api/collection/resource_id HTTP/1.1
Accept: [MIME type]
Content-Type: [MIME type]

[body]
这不包括 API 用户试图修改的“不可变”的资源属性。如果试图修改一个强制不可变的资源属性,API 会在响应的内容中包括一个错误信息。
不包括在表述中的属性将不会被修改。

7.4.4. 删除一个资源

使用发送到 URI 的 DELETE 请求来删除一个资源。
包括一个 Accept HTTP 头来定义响应格式的 MIME 类型。
DELETE /api/[collection]/[resource_id] HTTP/1.1
Accept: [MIME type]
在一些情况下,需要在 DELETE 请求的可选内容项中指定额外的属性。当 DELETE 请求带有可选内容项时,它将需要一个 Content-Type 头来告诉 API 内容项中的 MIME 类型表述。如果 DELETE 请求不包括内容项,将不需要 Content-Type 头。

7.4.5. 子集合关系

子集合关系定义了资源和子集合间的分级结构。子集合存在于它们的父资源中,或它们所具有的含义需要放在它们的父资源中来理解。例如,一个虚拟机包括网络接口,这意味着 API 对虚拟机资源和这个网络接口子集合建立映射关系。
子集合有以下的关系类型:
  • 一个父资源可以包括多个子资源,一个子资源也可以有多个父资源。例如,一个虚拟机可以包括多个磁盘,而一些磁盘也可以在多个虚拟机间共享。
  • 被映射的资源只依赖于一个父资源,如果没有父资源,这个资源将不存在。例如,一个虚拟机和它的快照间的关系。
  • 被映射的资源独立存在于父资源,但是数据间仍然保持着联系。例如,一个集群和一个网络的关系。
API 使用 link rel= 属性定义了一个资源和一个子集合的关系:
GET /api/collection/resource_id HTTP/1.1
Accept: application/xml

HTTP/1.1 200 OK
Content-Type: application/xml

<resource id="resource_id" href="/api/collection/resource_id">
    ...
    <link rel="subcollection"
      href="/api/collection/resource_id/subcollection"/>
    ...
</resource>
API 用户查询子集合。
GET /api/collection/resource_id/subcollection HTTP/1.1
Accept: application/xml

HTTP/1.1 200 OK
Content-Type: application/xml

<subcollection>
    <subresource id="subresource_id"
      href="/api/collection/resource_id/subcollection/subresource_id">
        ...
    </subresource>
    ...
</subcollection>

7.4.6. XML 项关系

XML 项中的链接是除子集合以外的另外一种代表资源间关系的方式。XML 项中的链接就是那些使用 "href" 属性的项。
XML 项中的链接被用来代表没有依赖关系,并且没有关联的资源间的 1:N 映射,如一个主机和一个集群的关系。
这种关系的实例包括:
  • 子资源中的一个资源到它的父资源的链接;或
  • 带有随意性关系的资源间的链接。

例 7.7. 从一个子集合资源到一个使用 XML 项的资源链接

GET /api/collection/resource_id/subcollection/subresource_id HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/xml

<subcollection>
    <subresource id="subresource_id"
      href="/api/collection/resource_id/subcollection/subresource_id">
        <resource id="resource_id" href="/api/collection/resource_id"/>
        ...
    </subresource>
</subcollection>

7.4.7. 操作

多数资源都会包括一组无法通过标准 HTTP 方法实现的操作。
<resource>
    ...
    <actions>
        <link rel="start" href="/api/collection/resource_id/start"/>
        <link rel="stop" href="/api/collection/resource_id/stop"/>
        ...
    </actions>
    ...
</resource>
API 通过一个对所提供的 URI 的 POST 请求来调用一个操作。POST 请求的正文(body)需要一个 action 表述来封装通用的参数和针对于特定操作的参数。

表 7.6. 通用操作参数

描述
asynctrue 如果服务器马上返回 202 Accepted,并且操作表述中包括了一个 href 链接。
grace_period宽限期(以毫秒为单位)。在初始化操作前需要等待的时间。
操作和它们的参数在相应的资源类型文档中有详细介绍。一些参数对于某些操作是必需的,如果没有使用它们就会导致一个 fault 响应。
因为 POST 请求需要在内容项中包括一个 XML 表述,所以操作也需要一个 Content-Type: application/xml 头。
当一个操作是异步进行的,202 Accepted 响应会提供一个用来监测操作状态的链接:
POST /api/collection/resource_id/action HTTP/1.1
Content-Type: application/xml
Accept: application/xml

<action>
    <async>true</async>
</action>

HTTP/1.1 202 Accepted
Content-Type: application/xml

<action id="action_id"
  href="/api/collection/resource_id/action/action_id">
    <async>true</async>
    ...
</action>
接下来的一个对操作 URI 的 GET 请求提供了异步操作的状态信息。

表 7.7. 操作状态

状态描述
pending任务还没有开始。
in_progress任务在进行中。
complete任务已经成功完成。
failed任务失败。返回的 action 表述将包括一个 fault 信息。
在任务完成后,操作会被保留一段时间。当这段保留时间过去后,随后的 GET 会是 301 Moved Permanently 来重定向到目标资源。
GET /api/collection/resource_id/action/action_id HTTP/1.1
Accept: application/xml

HTTP/1.1 200 OK
Content-Type: application/xml

<action id="action_id"
  href="/api/collection/resource_id/action/action_id">
    <status>
        <state>pending</state>
    </status>
    <link rel="parent" /api/collection/resource_id"/>
    <link rel="replay" href="/api/collection/resource_id/action"/>
</action>
一个操作的表述还会包括由 rel 属性指定的一些链接:

表 7.8. 操作关系

类型描述
parent连接回这个操作资源的链接。
replay连接回原始操作 URI 的链接。到这些 URI 的 POST 请求会重新初始化这个操作。

7.4.8. 权限

每个资源都包括了一个 permissions 子集合。每个 permission 都会包括一个 user、一个分配的 role 以及特定的资源。例如:
GET /api/collection/resource_id/permissions HTTP/1.1
Accept: application/xml

HTTP/1.1 200 OK
Content-Type: application/xml

<permissions>
    <permission id="permission-id"
      href="/api/collection/resource_id/permissions/permission_id">
        <role id="role_id" href="/api/roles/role_id"/>
        <user id="user_id" href="/api/users/user_id"/>
        <resource id="resource_id" href="/api/collection/resource_id"/>
    </permission>
    ...
</permissions>
当一个 API 用户向资源的 permissions 子集合发送一个带有 permission 表述和一个 Content-Type: application/xml 头的 POST 请求时,资源会获得一个新权限。每个新权限都需要一个 role 和一个 user
POST /api/collection/resource_id/permissions HTTP/1.1
Content-Type: application/xml
Accept: application/xml

<permission>
    <role id="role_id"/>
    <user id="user_id"/>
</permission>

HTTP/1.1 201 Created
Content-Type: application/xml

<permission id="permission_id"
  href="/api/resources/resource_id/permissions/permission_id">
    <role id="role_id" href="/api/roles/role_id"/>
    <user id="user_id" href="/api/users/user_id"/>
    <resource id="resource_id" href="/api/collection/resource_id"/>
</permission>

7.4.9. 错误处理

一些错误需要除标准 HTTP 状态代码外的其它一些信息。例如,API 会在响应信息的正文中包括一个 fault 表述来包括一个失败的资源状态更新或操作。这个表述会包括一个 reasondetail 字符串。客户端需要根据响应状态代码, fault 信息中的资源表述来对错误进行处理。相关的详细信息可在相应资源的文档中获得。
PUT /api/collection/resource_id HTTP/1.1
Accept: application/xml
Content-Type: application/xml

<resource>
    <id>id-update-test</id>
</resource>

HTTP/1.1 409 Conflict
Content-Type: application/xml

<fault>
    <reason>Broken immutability constraint</reason>
    <detail>Attempt to set immutable field: id</detail>
</fault>