RESTful API:具有共享或特定URL的HTTP动词?


25

创建RESTful API时,应该在相同的URL上使用HTTP动词(如果可能),还是在每个操作中创建特定的URL?

例如:

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

或使用特定的网址,例如:

GET     /items            # Read all items
GET     /item/:id         # Read one item
POST    /items/new        # Create a new item
PUT     /item/edit/:id    # Update one item
DELETE  /item/delete/:id  # Delete one item

Answers:


46

在后一种方案中,将动词保留在资源的URL中。应避免这种情况,因为应将HTTP动词用于此目的。拥抱基础协议,而不是忽略,复制或覆盖它。

只要看一下DELETE /item/delete/:id,您就可以在同一请求中两次放置相同的信息。这是多余的,应该避免。就个人而言,我会对此感到困惑。API实际上是否支持DELETE请求?如果我将其放置delete在URL中并改用其他HTTP动词怎么办?它会匹配什么吗?如果是这样,将选择哪一个?作为设计合理的API的客户,我不必问这样的问题。

也许您需要它来以某种方式支持无法发出DELETEPUT请求的客户端。如果是这种情况,我会在HTTP标头中传递此信息。一些API X-HTTP-Method-Override为此目的使用标头(无论如何我都觉得这很丑陋)。我当然不会将动词放在路径中。

去做

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

这些动词重要的是,它们已经在HTTP规范中进行了很好的定义,并且与这些规则保持一致,因此您可以使用缓存,代理以及应用程序外部的其他工具(这些工具可以理解HTTP的语义,但不能理解应用程序的语义) 。请注意,应避免将它们包含在URL中的原因不是关于需要可读URL的RESTful API。这是关于避免不必要的歧义。

而且,只要不违反HTTP规范,RESTful API可以将这些动词(或其任何子集)映射到任何应用程序语义集。例如,完全有可能构建一个仅使用GET请求的RESTful API,前提是它所允许的所有操作都是安全幂等的。上面的映射仅仅是适合您的用例并符合规范的示例。不一定必须是这样。

还请注意,真正的RESTful API绝不需要程序员阅读可用URL的大量文档,只要您遵守HATEOAS(超文本作为应用程序状态的引擎)原理,这是REST的核心假设之一。只要客户端应用程序可以理解并使用它们来找出可能的应用程序状态转换,这些链接就可能是人类所无法理解的。


4
如果没有PUTand DELETE,我宁愿将其添加到路径中,而不要使用查询字符串来区分它。这不是对现有操作的查询字符串修改;这是一个单独的操作。
罗伯特·哈维

4
@RobertHarvey在这种情况下,我还是称其为hack。就像您说的那样,这是一项操作,而在设计旨在实现RESTful的API时,这并不是我要做的事情。将其放置在查询字符串中似乎侵入性较小。它确实可以防止缓存,但我认为无论如何都不应缓存对此类请求的响应。它还使API的使用者可以轻松地指示该方法,而无需解析或构造URL。理想情况下,真正的RESTful API应该提供超链接,而无需客户端自己构建URL。
toniedzwiedz 2014年

如果您没有所有动词,那么它也不是完全RESTful的,是吗?
罗伯特·哈维

@RobertHarvey是正确的,但我将其视为后备,而不是预期的设计。我认为该API应该支持实际的HTTP方法,并且如果某些客户端由于某种原因无法实现它们,则可以将其用法替换为这些查询参数。代理甚至可以即时捕获这些请求,并使用真正的HTTP动词将请求转换为请求,因此服务器甚至不需要关心。几乎没有API是真正的RESTful。当涉及通用Web API时,确实是一个问题。就个人而言,我会使用干净的URL。更容易理解恕我直言。
toniedzwiedz 2014年

1
@RobertHarvey如所解释的那样,这几乎不是使用它们的预期方式。当您必须克服客户的限制时,我发现这是两个弊端较小的地方。我确实记得阅读过有关此类API的文档,但我必须在浏览器历史记录/书签中进行一些挖掘才能找到它。现在我想到了,在这种情况下,头文件可能会更好。你同意吗?
toniedzwiedz 2014年

14

第一个。

URI / URL是资源标识符(名称中的提示:统一资源标识符)。按照第一个约定,执行“ GET / user / 123”时要讨论的资源和执行“ DELETE / user / 123”时要讨论的资源显然是相同的资源,因为它们具有相同的URL。

使用第二种约定,您不能确定“ GET / user / 123”和“ DELETE / user / delete / 123”实际上是同一资源,并且似乎暗示您要删除相关资源而不是资源本身,因此,删除/user/delete/123实际上是deletes会令人惊讶/user/123。如果所有操作都在不同的URL上进行,则URI不再充当资源标识符。

当您说时DELETE /user/123,您说的是“删除ID为123的用户记录”。如果说DELETE /user/delete/123,则似乎意味着“删除'ID为123的用户删除记录'”,这可能不是您想说的。并且即使在这种情况下使用更正确的动词:“ POST / user / delete / 123”(表示“执行附加到ID为123的用户删除程序的附加操作)”,仍然是一种回旋方式来删除记录(这类似于英语中动词的名词化)。

您可以考虑使用URL的一种方法是将其像指向对象和资源的指针一样视为面向对象编程中的对象。当您执行GET /user/123DELETE /user/123,您可以将它们视为object:中的方法[/user/123].get()[/user/123].delete()其中[]就像是指针解引用运算符,但用于URL(如果您知道一种具有指针的语言)。REST的基本原理之一是统一接口,即具有少量且有限的动词/方法集,这些动词/方法适用于庞大的资源/对象网络中的所有事物。

因此,第一个更好。

PS:当然,这是以最纯粹的方式来看待REST。有时,实用性胜过纯粹性,您需要为脑残的客户或框架做出让步,使其难以进行适当的REST。


面向对象示例的+1 :)
53777A 2014年

6

(对不起,我第一次错过了(2)中的/ edit /和/ delete / ...)

URI的思想是它是可寻址资源标识符,而不是方法调用。因此,URI应该指向特定资源。而且,如果您引用URI,则应该始终获取相同的资源。

也就是说,您应该以与考虑数据库中行的主键相同的方式来考虑URI。它唯一地标识一些东西:通用资源标识符。

因此,无论您使用复数还是单数,URI都应该是一个标识符而不是一个Invocation。您要尝试执行的方法就是:GET(获取),PUT(创建/更新),DELETE(删除)或POST(其他所有方法)。

因此,“ / item / delete / 123”破坏了REST,因为它没有指向资源,而更多是方法调用。

(此外,从语义上讲,您应该能够获取URI,确定它已经过时,然后删除相同的 URI,因为它是一个标识符。如果GET URI没有“ / delete /”,而DELETE则具有,那么就会违反HTTP语义。您在每个资源中广播2个或更多URI,其中1个会这样做。)

现在,作弊是这样的:没有真正明确定义什么是资源,什么不是资源,因此REST中的常见特性是定义“处理名词”并将URI指向该资源。这几乎是一个文字游戏,但它满足语义。

因此,例如,如果由于某些原因您真的不能使用此功能:

DELETE /items/123

您可以向世界宣告您拥有“删除者”处理资源并使用

POST /items/deletor  { id: 123 }

现在,这看起来很像RPC(远程过程调用),但是它陷入了HTTP规范中命名的POST规范的“数据处理”子句的巨大漏洞。

但是,这样做是一种例外,如果您可以使用通用的PUT进行创建/更新,使用DELETE进行删除,而使用POST进行附加,创建以及其他所有操作,则应该使用,因为这是HTTP的更标准用法。但是,如果您遇到诸如“ commit”或“ publish”或“ redact”之类的棘手案例,那么使用处理器名词的案例就可以满足REST纯粹主义者的要求,并且仍然为您提供所需的语义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.