在REST中,如何响应只有一个子实体且已经存在一个子实体的POST?

问题描述

我有一个vehicles资源作为聚合的根实体。每辆车都可以有零个或一个engines作为子资源/实体。

如果#947车辆存在,但没有引擎,我可以说:

POST /vehicles/947/engines         /* create an engine with id=0 */

GET /vehicles/947/engines          /* read an array bearing a single engine at index 0 with id=0*/

GET /vehicles/947/engines/0        /* read the engine explicitly by id */

如果执行了上述操作,但随后又执行了该操作,我应该返回什么?

POST vehicles/947/engines

我已经有用于947车辆的发动机,我不能再允许其他发动机。应该返回什么状态?

我看过的一些可能性:

  • 403禁止访问”-此wikipedia article指示应发布“ 403禁止访问”。但是,this MDN page似乎表明“ 403 Forbidden”与权限问题有关。
  • 406不可接受”-Per MDN,这似乎更针对标头限制导致的情况。
  • 409冲突”-Per MDN,也许吧。

我应该以不同的方式构建REST路径吗?我认为所示的结构是合理的。 Engines确实是其自己的实体,并附加到车辆上。每个引擎都有很多我真的不想扔进汽车的属性。这些属性属于引擎。

有想法吗?

解决方法

第一点:IANA status code registry列出了每个状态码的语义权威参考。大多数常见的是由最新的HTTP规范定义的;今天的意思是RFC 7231

第二点:状态代码是元数据,提供有关对通用组件(例如Web浏览器或缓存)的响应语义的提示。以标准方式使用状态代码意味着我们的API资源看起来就像网络上的任何其他页面。

换句话说,我们可以通过考虑“ Web服务器会做什么?”来回答类似的问题。或“状态码如何改变通用组件的行为?”

您的示例的合理选择:

  • 403“我了解您的请求,但我拒绝批准它。”
  • 405“此资源目前不支持此方法”
  • 409“此编辑与资源的当前状态冲突”

405是“默认情况下可缓存的”,这是区分它们的一种方法。此外,该状态码还需要一个Allow标头来描述当前支持的方法。

否则,这些代码之间没有太大区别-浏览器不会像对待缓存一样对待它们。

要考虑的另一点是您选择的代码对操作员或最终阅读访问日志的任何机构的影响。访问日志中的403可能意味着许多不同的事物,其中409具有较少与其标准语义一致的选项。

我应该以不同的方式构造REST路径吗?

也许。 REST不关心实体,它关心的是文档resources在很大程度上是文档的概括)。

那么关于发动机的文件是否与关于车辆的文件相同,或者它们应该是两个单独的文件(也许通过链接连接)?应该将它们一起缓存(意味着使一个文档无效也使另一文档无效)还是单独缓存(一个文档频繁更改,而另一个很少)?

这是我们在决定是否将Java脚本放入HTML文档中的script标签中以及是否具有从HTML文档到javascript的链接时所进行的折衷。在正确的条件下,这两个答案都是有意义的。

URI拼写很好。如果只有一个引擎资源,那么您可以考虑使用标识符/vehicles/947/engine。将引擎文档存储在引擎文档集合/engines/947中也很有意义。如果您希望使用dot segments来标识其他文档,那么将标识符放在相同的层次结构中会更加方便。