【问题标题】:REST resource path designREST资源路径设计
【发布时间】:2014-01-30 19:44:05
【问题描述】:

我正在开发一项 REST 服务,并且我正在努力遵守 Roy Fielding 博士的约定和准则。

我将我的服务想象成一个公开一组资源的端点。资源由 URI 标识,api 客户端可以使用 HTTP 语义来操作资源(即,不同的 HTTP 动词映射到 URI 上的相应操作)。

指南规定这些 URI 应以分层方式定义,反映对象层次结构。这在资源创建中很有用,因为在后端我们需要数据来执行创建操作。但是,在进一步的操作中,URI 中包含的很多信息甚至不会被服务使用,因为通常仅资源 Id 就足以唯一标识操作目标。

一个示例:考虑一个公开产品创建和管理的 API。还要考虑产品与品牌相关联。在创建时,执行以下操作是有意义的: HTTP POST /品牌/{brand_id}/产品 [包含创建产品所需输入的正文]

该创建返回一个 HTTP 201,该 HTTP 201 使用一个位置标头创建,该标头公开了新创建的产品的位置。

在进一步的操作中,客户可以通过以下方式访问产品: HTTP PUT /品牌/{brand_id}/Product/{product_id} HTTP 删除 /Brand/{brand_id}/Product/{product_id} 等等

但是,由于产品 ID 在产品范围内是通用的,因此可以像这样执行以下操作: /产品/{product_id} 出于连贯性的原因,我只保留 /Brand/{brand_id} 前缀。事实上,服务忽略了品牌 ID。您是否认为这是一个很好的做法,并且为了保持清晰、明确的 ServiceInterface 定义是合理的?这样做有什么好处,这是否可行?

也将不胜感激任何有关 URI 定义最佳实践的指针。

提前致谢

【问题讨论】:

标签: rest uri


【解决方案1】:

你是说:

指南规定这些 URI 应以分层方式定义,反映对象层次结构。

虽然通常以这种方式完成,但它与 RESTful API 设计并不真正相关。 Roy Fielding 有一个nice article 解决关于 REST 的常见误解。他甚至说:

REST API 不得定义固定的资源名称或层次结构(客户端和服务器的明显耦合)。

除了初始 URI 之外,应在没有任何先验知识的情况下输入 REST API……

因此,请勿在 URL 中对应在资源中传递的信息进行编码。即使您将所有 URL 替换为人工和无意义的 URI,RESTful API 也应该可以工作。 (我喜欢任何人都可以理解的 URI,但作为检查“RESTfullness”的心理练习,它非常好。)

为对象“层次结构”建模 URI 的问题是层次结构并不像看起来那么明显。 (教师、课程和学生之间的对象层次结构是什么?)。通常,对象处于关系网络中,并不明显属于另一个对象之下。一个产品可能属于一个品牌,但您可能有多个供应商(涵盖多个品牌的产品子集)。 REST 非常适合表达复杂的关系网络。整个互联网/网络都是这样工作的。

无需在层次结构中编码关系,只需在资源中定义一个指向相关对象的超链接。

对于您的具体示例,我将使用 POST /product/ 创建新产品,并在创建产品时在资源表示中包含指向您的 /brand/xzy 的链接。

如果您想知道为特定品牌定义了哪些产品,只需在返回的 GET /brand/xzy 表示中包含链接列表。如果您希望有一个明确的资源来表示这种关系,您仍然可以将 GET /brand/{id}/products 定义为 URL(或 /brandproducts/xzy 或 /34143453453)并将其作为品牌资源中的链接返回。

不要过多考虑 URI 的设计,多考虑您在资源中提供的信息。确保它提供了指向您的客户端在从您的 API 接收后可能想要查看或操作的所有资源表示的链接。

【讨论】:

    【解决方案2】:

    我认为这是关键评论:

    产品与品牌相关联。

    关联这个词告诉我你需要将资源链接在一起。所以,假设品牌和产品之间存在关联。正如您所描述的,每个 resource 都有自己的一组方法(GET、PUT 等),但表示应该有 link 到描述其关联的其他资源。链接的位置取决于关联的类型(一对一、一对多、多对一、多对多)和方向。

    例如,假设有一个对该产品的规范请求到 api.example.com:

    GET /product/12345
    

    它返回该产品的一些表示。为简单起见,我将使用 XML 来表示,但它可以是 XHTML、JSON 或任何您需要的。因此,产品 12345 的简单表示:

    HTTP/1.1 200 OK
    Content-Type: application/xml; charset=utf-8
    Content-Length: ...
    
    <?xml version="1.0"?>
    <Product href="http://api.example.com/product/12345" rel="self" type="application/xml"/>
      <Name>Macaroni and Cheese</Name>
      <Brand href="http://api.example.com/brand/7329" type="application/xml"/>
      <Manufacturer href="http://api.example.com/manufacturer/kraft" rel="parent" type="application/xml"/>
    </Product>
    

    如您所见,我将链接嵌入到描述每种关系的产品 12345 的表示中。只要有可能,我就会尽可能地遵循HATEOAS 约束:

    • 当前资源和相关资源之间存在显式链接。
    • 可选的关系描述(“rel”)。 “self”和“parent”是关于当前资源和链接引用的资源的关系的描述。
    • 应请求的可选首选 MIME 类型。这描述了在提出后续请求时客户应该期望的文档类型。
    • 不透明的 URL,而不是原始标识符。客户端可以直接“导航”到 URL,而无需使用某种约定来构建它们。请注意,网址并不总是需要包含唯一的数据库标识符或密钥(例如“/brands/kraft”)。

    为了扩展一些高级概念,假设产品具有其他关系。也许产品具有等级关系或产品取代了其他产品。所有这些复杂的关系都可以用链接来表示。因此,产品 12345 的高级表示:

    HTTP/1.1 200 OK
    Content-Type: application/xml; charset=utf-8
    Content-Length: ...
    
    <?xml version="1.0"?>
    <Product href="http://api.example.com/product/12345" rel="self" type="application/xml"/>
      <Name>Macaroni and Cheese</Name>
      <Brand href="http://api.example.com/brand/7329" rel="parent" type="application/xml"/>
      <Manufacturer href="http://api.example.com/manufacturer/kraft" type="application/xml"/>
      <!-- Other product data -->
      <Related>
        <Product href="http://api.example.com/product/29180" rel="prev" type="application/xml"/>
        <Product href="http://api.example.com/product/39201" rel="next" type="application/xml"/>
      </Related>      
    </Product>
    

    在此示例中,我使用“prev”和“next”来表示产品链。 “prev”可以解释为“superseded”,“next”可以解释为“superseded-by”。您可以使用“superseded”和“superseded-by”作为 rel 值,但通常使用“prev”和“next”。这完全取决于您。

    【讨论】:

    • 看来您的建议是:保持资源路径简单,并在每个资源成员中包含链接。您只处理 Get 案例。你将如何创造一个新产品?即,您会在创建时间指定谁,与新产品相关的制造商是什么?将其包含在请求正文中?这听起来不对。听起来该信息将包含在资源 URI 中
    • 链接将包含在请求正文中,因为那是该资源的表示。您必须对请求正文执行适当的检查,以确保制造商和品牌确实存在。
    猜你喜欢
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    相关资源
    最近更新 更多