用于复杂操作的 REST 端点
REST endpoint for complex actions
我有一个 REST API,它提供从数据库到前端 React 应用程序和 Android 应用程序的数据。
API 每个模型都有多个公共端点:
- GET /model/<id>
检索单个对象
- POST /model
创造
- PATCH /model/<id>
更新单个模型
- GET /model
列出对象
- DELETE /model/<id>
删除对象
目前我正在开发一个 Android 应用程序,我发现这样的方案让我对 API 发出许多额外的请求。例如,每个 Order 对象都有一个 user_creator 条目。所以,如果我想删除指定用户创建的所有订单,我需要
1) 列出所有用户 GET /user
2) Select 我需要的
3) 列出他创建的所有订单GET /order?user=user_id
4) Select 我要删除的订单
5)删除订单DELETE /order/<id>
我想知道是否可以添加几个端点,例如 GET /order/delete?user=user_id
。通过这样做,我可以摆脱操作 4 和 5。所有的过滤都将在后端完成。然而,在我看来,这是一个糟糕的架构解决方案,因为我之前使用的所有 APIs 都没有这样的方法,而且所有的过滤、排序和其他 "beautifying" 东西通常都在 API 用户端,不是后端。
在您的回答中,请提供您认为对这个问题最好的解决方案,并至少简要说明您的观点,以便我从中学习
HTTP的应用领域是通过网络传输文档。您的“REST API”是一个类似于文档存储的外观,作为传输文档的副作用 执行有用的工作。参见 Jim Webber (2011)。
所以基本的习惯用法是我们 post 一个文档,或者我们向现有文档发送一堆编辑,服务器解释这些更改并做一些有用的事情。
因此,基于现有远程创作语义的简单协议可能看起来像
GET /orders?user=user_id
Make local edits to the representation of that list provided by the server
PUT /orders?user=user_id
交易双方都需要理解如何做到这一点的语义。也许您从列表中删除了不需要的项目?也许列表中的每条记录都有一个状态条目,并且您将状态从活动更改为已过期。
在网络上,我们倾向于使用表单提交而不是远程创作语义。你从某个地方得到一张空白表格,你自己填写,你 post 它到指定的收件箱,负责处理该收件箱的人完成工作。
因此,我们将一个空白表单加载到浏览器中,对其进行更改,然后我们 post 将其添加到表单中列出的资源中。
GET /the-blank-form?user=user_id
Make changes in the form...
POST ????
目标 uri 应该是什么?网络浏览器不在乎;它只是将表单提交给它收到的表示指定的任何目标。一个答案可能是将它直接发送回我们得到它的地方:
POST /the-blank-form?user=user_id
而且效果很好(只要您正确管理元数据)。另一种可能性是将更改发送到您希望反映这些更改的资源:
POST /orders?user=user_id
事实证明 也能正常工作。 HTTP 在规范中内置了有趣的 cache invalidation 语义,因此我们可以通过使用与 POST 调用的目标相同的资源来确保客户端的陈旧副本或订单集合资源无效。
Currently my API satisfies the table from the bottom of the REST, so, any extra endpoint will break it. Will it be fatal or not, that's the question.
不,没问题 -- 只需 add/extend 在适当资源上的 POST 处理程序来处理新语义。
更长的答案:维基百科中的 table 很好地代表了常见做法;但常见的做法并不完全正确。部分问题在于 REST 包含一个 统一接口 。除其他外,这意味着所有资源都理解相同的消息语义。 “集合资源”与“成员资源”的概念在 REST 中不存在——两者的语义相同。
另一种说法是,通用组件永远不知道与之对话的资源是集合还是成员。所有不安全的方法 (POST/PUT/PATCH/DELETE/etc) 都隐含 invalidation 目标 uri 的表示。
现在 POST 碰巧意味着“做一些尚未标准化的事情”——参见 Fielding 2009。这是具有最少语义约束的方法。
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. -- RFC 7231
POST 处理程序根据请求负载的内容进行分支是完全没问题的;如果你看到 X,就创建一些东西,如果你看到 Y,就删除其他东西。这类似于有两个不同的网络表单,具有不同的语义,提交到相同的目标资源。
把你的问题孤立起来:
- 您有一个订单集合和一个用户集合
- 用户 1..* 订单
- 您想删除给定用户 ID 的所有订单
我会使用以下 URI:
// delete all orders for a given user
POST /users/:id/orders/delete
自然地,这显示了用户和订单之间的关系,并且不言自明您只处理与特定用户关联的订单。此外,如果该操作会对服务器产生副作用,那么您应该 POST 而不是 GET(读取资源永远不会更改服务器)。同样的逻辑可以用来创建一个端点来只拉取用户订单,例如
// get all orders for a given user
GET /users/:id/orders
我有一个 REST API,它提供从数据库到前端 React 应用程序和 Android 应用程序的数据。
API 每个模型都有多个公共端点:
- GET /model/<id>
检索单个对象
- POST /model
创造
- PATCH /model/<id>
更新单个模型
- GET /model
列出对象
- DELETE /model/<id>
删除对象
目前我正在开发一个 Android 应用程序,我发现这样的方案让我对 API 发出许多额外的请求。例如,每个 Order 对象都有一个 user_creator 条目。所以,如果我想删除指定用户创建的所有订单,我需要
1) 列出所有用户 GET /user
2) Select 我需要的
3) 列出他创建的所有订单GET /order?user=user_id
4) Select 我要删除的订单
5)删除订单DELETE /order/<id>
我想知道是否可以添加几个端点,例如 GET /order/delete?user=user_id
。通过这样做,我可以摆脱操作 4 和 5。所有的过滤都将在后端完成。然而,在我看来,这是一个糟糕的架构解决方案,因为我之前使用的所有 APIs 都没有这样的方法,而且所有的过滤、排序和其他 "beautifying" 东西通常都在 API 用户端,不是后端。
在您的回答中,请提供您认为对这个问题最好的解决方案,并至少简要说明您的观点,以便我从中学习
HTTP的应用领域是通过网络传输文档。您的“REST API”是一个类似于文档存储的外观,作为传输文档的副作用 执行有用的工作。参见 Jim Webber (2011)。
所以基本的习惯用法是我们 post 一个文档,或者我们向现有文档发送一堆编辑,服务器解释这些更改并做一些有用的事情。
因此,基于现有远程创作语义的简单协议可能看起来像
GET /orders?user=user_id
Make local edits to the representation of that list provided by the server
PUT /orders?user=user_id
交易双方都需要理解如何做到这一点的语义。也许您从列表中删除了不需要的项目?也许列表中的每条记录都有一个状态条目,并且您将状态从活动更改为已过期。
在网络上,我们倾向于使用表单提交而不是远程创作语义。你从某个地方得到一张空白表格,你自己填写,你 post 它到指定的收件箱,负责处理该收件箱的人完成工作。
因此,我们将一个空白表单加载到浏览器中,对其进行更改,然后我们 post 将其添加到表单中列出的资源中。
GET /the-blank-form?user=user_id
Make changes in the form...
POST ????
目标 uri 应该是什么?网络浏览器不在乎;它只是将表单提交给它收到的表示指定的任何目标。一个答案可能是将它直接发送回我们得到它的地方:
POST /the-blank-form?user=user_id
而且效果很好(只要您正确管理元数据)。另一种可能性是将更改发送到您希望反映这些更改的资源:
POST /orders?user=user_id
事实证明 也能正常工作。 HTTP 在规范中内置了有趣的 cache invalidation 语义,因此我们可以通过使用与 POST 调用的目标相同的资源来确保客户端的陈旧副本或订单集合资源无效。
Currently my API satisfies the table from the bottom of the REST, so, any extra endpoint will break it. Will it be fatal or not, that's the question.
不,没问题 -- 只需 add/extend 在适当资源上的 POST 处理程序来处理新语义。
更长的答案:维基百科中的 table 很好地代表了常见做法;但常见的做法并不完全正确。部分问题在于 REST 包含一个 统一接口 。除其他外,这意味着所有资源都理解相同的消息语义。 “集合资源”与“成员资源”的概念在 REST 中不存在——两者的语义相同。
另一种说法是,通用组件永远不知道与之对话的资源是集合还是成员。所有不安全的方法 (POST/PUT/PATCH/DELETE/etc) 都隐含 invalidation 目标 uri 的表示。
现在 POST 碰巧意味着“做一些尚未标准化的事情”——参见 Fielding 2009。这是具有最少语义约束的方法。
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. -- RFC 7231
POST 处理程序根据请求负载的内容进行分支是完全没问题的;如果你看到 X,就创建一些东西,如果你看到 Y,就删除其他东西。这类似于有两个不同的网络表单,具有不同的语义,提交到相同的目标资源。
把你的问题孤立起来:
- 您有一个订单集合和一个用户集合
- 用户 1..* 订单
- 您想删除给定用户 ID 的所有订单
我会使用以下 URI:
// delete all orders for a given user
POST /users/:id/orders/delete
自然地,这显示了用户和订单之间的关系,并且不言自明您只处理与特定用户关联的订单。此外,如果该操作会对服务器产生副作用,那么您应该 POST 而不是 GET(读取资源永远不会更改服务器)。同样的逻辑可以用来创建一个端点来只拉取用户订单,例如
// get all orders for a given user
GET /users/:id/orders