据我所知,RESTful API中使用了四种方法:
获取资源的GET 用于更新资源的 POST 用于创建或替换资源的 PUT DELETE 用于删除资源。
假设我们有一个名为apple的资源,我们可以通过多种方式“更新”它。例如,将其切成薄片,切片或制成苹果汁 这三种不同的更新操作中的每一种都采用不同的参数,并且它们的API中,共同的部分将是:
POST /apple HTTP/1.1
Host: www.example.com
<different combination of arguments>
在这种情况下,三个API共享相同的URI和相同的请求方法,它们的唯一区别是参数。我认为这迫使后端准备好接受这些参数的联合集,并且为了区分实际请求的动作,后端需要检查参数的组合。它太复杂而且不优雅。
所以我的问题是:
在这个苹果案例中,如何设计出一套优雅的RESTful API,使后端可以轻松处理。
答案 0 :(得分:15)
首先,尽量避免将HTTP方法与CRUD操作混淆。我相信这是REST中混淆的主要原因。 HTTP方法不像这样干净地转换为CRUD操作。我在这里有一个详细的答案:
简而言之。
现在,在后端方面,尝试将REST资源更像是一个状态机,您可以使用这些方法来强制转换而不是使用方法的对象。这样您就可以将实现集中在资源本身上,而不是与协议的交互上。例如,您可以直接从方法的有效负载中更改对象的属性,然后调用一个方法来检测所需的转换。
例如,您可能会认为苹果有三种状态,整个,削皮,切片和榨汁。您可以使用方法的标准化行为在状态之间转换。
例如:
GET /apple
{"state": "whole",
"self": "/apple"}
然后你要切片。你可以这样做:
PUT /apple
{"state": "sliced"}
或者您可能会这样做:
PATCH /apple
{"from_state": "whole", "to_state": "sliced"}
甚至可能是:
POST /apple
{"transition": "slice"}
这个想法是实现可以是通用的,你不必过于担心将资源耦合到HTTP方法。
只要您的资源实现了解当apple.state
更改为其他内容时,它应检测发生了哪些更改并执行适当的转换,您就完全脱离了协议。使用什么方法并不重要。
我相信这是最优雅的解决方案,让后端更容易处理。您可以实现您的对象,而无需过多担心协议。只要对象可以在状态之间转换,任何可以影响这些转换的协议都可以使用它们。
答案 1 :(得分:5)
我的RESTful HTTP API与您的非常相似。我有:
GET 获取资源 POST ,用于将新资源附加到集合中 用于替换资源的 PUT (包括截断集合) 删除以删除资源 用于更新资源的 PATCH LINK 用于指示两个资源之间的关系 取消链接用于删除两个资源之间的关系。
'leaf'资源也可以被认为是一个集合。
例如,假设您有/fruits
而 POST apple
到该集合资源,则会返回
201 Created
Location: /fruits/apple
以同样的方式,您可以将/fruits/apple
视为其属性的集合,因此:
GET /fruits/apple
->
colour=red&diameter=47mm
GET /fruits/apple/colour
->
red
GET /fruits/apple/diameter
->
47mm
因此:
PUT /fruits/apple/slices
"12"
->
201 Created
GET /fruits/apple
->
colour=red&diameter=47mm&slices=12
总而言之,我建议将您的操作表示为名词,并将这些名词定位为您要将操作应用到的资源的子资源。
答案 2 :(得分:1)
从资源角度思考。 Apple是一个资源。
要添加一个或多个苹果以列出“/ apples”,请使用POST。 REST样式允许发布数组。
POST /apples HTTP/1.1
Host: www.example.com
现在假设您有一个ID为123的苹果。您可以使用“/ apple / 123”上的方法GET获取详细信息。
GET /apples/123 HTTP/1.1
Host: www.example.com
要对apple 123进行任何更改,只需直接发送到它。
PUT /apples/123 HTTP/1.1
Host: www.example.com
削减它,切片,或者做成苹果汁 - 这些都基本上改变了苹果123的一些属性。正如你所说的那样(正确),PUT不同的属性组合。
答案 3 :(得分:0)
我认为这取决于实施者的决定,但我看到了两种方法。从单一责任的角度来看,为这些不同的操作提供单独的服务可能是有意义的。
但是,如果你坚持单一服务,我猜你可以传递一个带有动作类型限定符的对象,以便于将请求委托给服务中的不同代码。然后,单个对象可以具有其他可选参数,以支持每个操作的数据需求。