我正在开发一项REST服务,我正在努力遵守Roy Fielding博士的惯例和指导方针。
我将我的服务描述为暴露一组资源的端点。资源由URI标识,并且api客户端可以通过使用HTTP语义来操纵资源(即,不同的HTTP谓词映射到URI上的相应操作)。
指南声明这些URI应以分层方式定义,反映对象层次结构。这在资源创建中很有用,因为在后端我们需要数据来执行创建操作。但是,在进一步的操作中,URI中包含的许多信息甚至都不会被服务使用,因为通常只有资源Id足以唯一地识别操作目标。
一个例子:考虑一个暴露产品创建和管理的Api。还要考虑产品与品牌相关联。在创建时,执行以下操作是有意义的: HTTP POST /品牌/ {brand_id} /产品 [包含创建产品所需输入的正文]
创建返回使用位置标头创建的HTTP 201,该位置标头公开新创建的产品的位置。
在进一步操作时,客户可以通过执行以下操作来访问产品: HTTP PUT /品牌/ {brand_id} / Product / {product_id} HTTP DELETE / Brand / {brand_id} / Product / {product_id} 等
但是,由于产品ID在产品范围内是通用的,因此可以执行以下操作: /产品/ {PRODUCT_ID} 出于连贯性原因,我只保留/ Brand / {brand_id}前缀。事实上,该服务忽略了品牌ID。你认为这是一个好的做法,并且为了保持一个明确,明确的ServiceInterface定义是合理的吗?这样做有什么好处,这是一种可行的方法吗?
关于URI定义最佳实践的任何指针都将受到赞赏。
提前致谢
答案 0 :(得分:8)
你在说:
指南声明这些URI应以分层方式定义,反映对象层次结构。
虽然它通常以这种方式完成,但它与RESTful API设计并不相关。 Roy Fielding有nice article解决有关REST的常见误解。在那里他甚至说:
REST API不能定义固定资源名称或层次结构(客户端和服务器的明显耦合)。
和
应输入REST API,除了初始URI之外没有任何先验知识......
因此,不要在URL中编码应该在资源内传递的信息。即使您使用人工和非感知URI替换所有URL,RESTful API也应该有效。 (我喜欢可以理解的URI,但是作为一种心理练习来检查你的“RESTfullness”它是非常好的。)
为对象“层次结构”建模URI的问题在于层次结构通常不像看起来那么明显。 (教师,课程和学生之间的对象层次结构是什么?)。通常,对象处于关系网中,并且不属于另一个对象之下。产品可能属于某个品牌,但您可能拥有多个供应商(涵盖多个品牌的产品子集)。 REST非常适合表达复杂的关系网。整个互联网/网络以这种方式工作。
不是在层次结构中编码关系,而是在资源中定义指向相关对象的超链接。
对于您的具体示例,我将使用POST / product /创建新产品,并在创建产品时在资源表示中包含指向/ brand / xzy的链接。
如果您想知道为特定品牌定义了哪些产品,只需在返回的GET / brand / xzy表示中包含一个链接列表。如果您想拥有代表此关系的显式资源,您仍然可以将GET / brand / {id} / products定义为URL(或/ brandproducts / xzy或/ 34143453453),并将其作为品牌资源中的链接返回。
不要过多考虑URI的设计,更多地考虑您在资源中提供的信息。确保它提供了客户端从API接收后可能要查看或操作的所有资源表示的链接。
答案 1 :(得分:1)
我认为这是关键评论:
产品与品牌相关联。
关联这个词告诉我你需要链接资源。所以,让我们说品牌和产品之间存在联系。每个资源都有自己的一组方法(GET,PUT等),但是表示应该有链接到描述其关联的其他资源。链接的位置取决于关联的类型(一对一,一对多,多对一,多对多)和方向。
例如,假设有一个针对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约束:
为了扩展一些先进的概念,让我们说产品有其他关系。也许产品具有等级关系或产品取代其他产品。所有这些复杂的关系都可以用链接表示。因此,产品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”可以解释为“被取代”,“next”被解释为“被取代”。您可以使用“取代”和“取代”作为rel值,但通常使用“prev”和“next”。这完全取决于你。