一个RESTful服务应该如何在可变资源上公开只读属性?

22

我正在设计一个服务的资源,它有一组可变属性和一组不可变属性(例如,status是由服务生成的,客户端无法更改)。

我需要在响应 GET 请求时包含它,但如果有人使用 PUT 请求发送资源,我不确定该怎么办。

强制调用者知道哪些属性是不可变的感觉不对,但默默地丢弃更新也不正确。对 PUT 请求做出更新后再将更新后的资源返回给调用者可能会解决问题,但这并不完美,因为调用者不应该进行其请求和服务响应之间的差异比较以查找是否接受了某个属性。

您对正确的前进方式有什么想法吗?

P.S. 我查看了如何更新REST资源?,但与此问题不同,并促进了过于啰嗦的API设计。

4个回答

12

我建议遵循 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10 的指南。HTTP 409的定义包括以下内容:

1)由于与资源当前状态的冲突,请求无法完成。

2)响应正文应包含足够的信息,以使用户能够识别冲突的源。

因此,由于不可变属性的更改问题与资源状态有关,似乎适用于HTTP 409。

至于如何向客户端传达问题,建议在响应正文中包含详细信息。

您也可以在表示本身(对于GET)中传达属性的可变性。例如:

<MyObject>
   <Foo>17</Foo>
   <Bar readOnly="true">22</Bar>
   ....

1
我认为这可能是正确的方式:如果他们试图更改不可变属性,则返回 409;如果他们不更改,则我们接受并悄悄丢弃它。最终状态符合他们的期望,属性保持只读。 - ehdv

3
您可以设计您的 API 响应,使只读属性实际上与可变属性分开。例如:
{
    id: 42,
    status: "terrific",
    properties: {
        // mutable properties here
    }
}

我既编写过这样的API,也使用过这样的API,效果都非常好。


9
我会对这种方法感到担忧,因为它将属性的特征放入了层次结构中。此外,可变性可能会发生变化,例如,如果某个资源的一部分由于某种原因被暂时锁定。 - ehdv

1
另一种解决方案是将只读字段作为HTTP标头发送回来。这样,您可以保持GET和PUT资源的相同。如果只读字段主要是“技术”字段,例如createTime,updateTime等,则这可能是最合理的选择。状态(Status)也可以视为技术字段吗?

1

1
"[HTTP PATCH]允许您精确定位要修改的属性。" 当然,这并不能帮助客户端知道哪些属性是可修改的,哪些是不可修改的,我真的不认为您的博客文章能够令人信服地证明忽略它不想更改的内容是可以接受的。我并不是说您错了,我只是认为您并没有必要证明某些主张。 - Matt Ball
补丁本身并不告诉客户端可以修改哪些字段。这需要在初始表示中添加超媒体元素:一个“表单”元素,解释哪些字段是不可变的,哪些是可变的。如果不可变字段集在运行时更改,则我只能看到这种解决方案。如果不可变字段集是固定的,则我认为客户端硬编码该知识没有任何问题。但是从更大的规模来看,超媒体是正确的选择:这将放松客户端-服务器耦合,并允许服务器随时间推移而不会破坏客户端。 - Jørn Wildt
如果我们在响应中添加超媒体元素,那么将所有内容放回PUT甚至变得更奇怪。我的意思是,为什么表单(和链接)元素应该被放回服务器(它们肯定是不可变的)?我认为这有利于PATCH(以及传统的POST与x-www-form-urlencoded键/值对或JSON ...但是像我在博客文章中提到的那样,空值处理成为一个问题)。 - Jørn Wildt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接