以RESTful方式接受选择的方法

5
我有一个简单的API,运作方式如下:
1.用户创建请求(`POST /requests`) 2.另一个用户检索所有请求(`GET /requests`) 3.然后为请求添加报价(`POST /requests/123/offers`) 4.原始用户现在可以看到为该请求提供的所有报价(`GET /requests/123/offers`)
我想要做的是,允许最初的用户接受一个请求,但我无法想出最佳的RESTFUL方法来实现它。
我应该使用PATCH动词吗?像 `PATCH /requests/123` 并要求补丁主体包含有效的报价ID?

我认为POST方法适用于接受请求,而对于创建请求和提供服务,您可以使用PUT方法。 - Shriike
2个回答

7
接受五次报价应该与接受一次报价具有相同的效果。它是幂等的,因此应该使用PUT。
您可能希望考虑为您的“请求”选择不同的名称。当我执行GET /requests/123时,我请求一个请求的响应?这可能会对客户端造成一些困惑。
此外,请尽量避免嵌套资源标识符。这可能会在以后给您带来问题。一个报价实际上不必“位于”相应的请求之下。当您以后想要将报价对应多个请求时会发生什么?
一个好的经验法则是,如果您认为“Gizmo”是entity-relationship model中的一个实体,则应该是根级别的URI,例如在GET /gizmos/17中,而不是GET /widgets/54/gizmos/17。一个常见的错误是说“每个Gizmo都有一个相关联的Widget,所以我应该将Gizmo URI嵌套为Widget URI的扩展”。

以下是我对操作的建议。您可能希望将一些ID引用替换为URI,但这取决于您。

POST /requests            --->   201 Created
                                 Location: /requests/123

GET /requests             --->   200 OK
                                 [
                                     {
                                         "requestId": 123,
                                         "offersUri": "/offers?requestId=123",
                                         ...
                                     },
                                     ...
                                 ]

POST /offers              --->   201 Created
{                                Location: /offers/456
    "requestId": 123,
    "amount": 300,
    ...
}

GET /offers?requestId=123 --->   200 OK
                                 [
                                     {
                                         "requestId": 123,
                                         "amount": 300,
                                         ...
                                     }
                                 ]

PUT /offers/456/approval   --->  204 No Content
PUT /offers/456/approval   --->  204 No Content
PUT /offers/456/approval   --->  204 No Content

1
感谢您提到深度嵌套标识符。这是REST架构初学者经常犯的错误。 - Chris Broski
谢谢! 您的回答似乎是正确的选择。我唯一的问题是关于报价列表。我应该如何提供一种简单的方法来检索请求的所有报价? 是否应该有一个用于过滤的url参数 GET /offers?request=123 or 我应该在请求中提供一个参数来嵌入这些数据 GET /request/123?embedded=true,以实际替换offerIds为实际报价? - Janis Peisenieks
1
@JanisPeisenieks 我觉得 GET /offers?requestId=123 比较合理。避免你后面的建议,即 GET /request/123?embedded=true。一个资源应该链接到其相关资源,而不是将其嵌入其中。事实上,你甚至可以考虑将请求资源改为以下形式,而不是我上面提出的形式:{"requestId":123,"offersUri":"/offers?requestId=123"} - Timothy Shields
我更喜欢以前的设计,其中优惠列表是请求GET数据的一部分。我看不出为什么除了请求信息外还需要单独的请求优惠资源,但我没有关于应用程序的所有细节。 - Chris Broski
1
@protonfish 优化和适当的概念设计并不一定相同。从问题中:“我无法找到最佳RESTful方式。”提供GET /offers/456GET /requests/123作为获取资源/offers/456的两种方式可能是一种优化,但我不认为这是很好的概念设计。报价不在请求的概念范围内。从性能角度来看,我完全同意您的观点。 - Timothy Shields
显示剩余4条评论

1

取决于“接受”(Acceptance)的性质。

如果“接受”只是一个报价的简单属性,我会将报价发送(Post)并将“接受”设置为True。

如果“接受”更加复杂,因此可以作为一种资源进行独立处理,我会将“接受”放入报价中(PUT /requests/123/offers/acceptance)。

如果存在拒绝或请求澄清报价的情况,我可能会将相关资源视为响应(Response),而不是接受,并将其放入(PUT /requests/123/offers/response)。


虽然不值得被踩,但你似乎将许多操作数据放在URI中,这违反了REST的标识和转换原则。 - Chris Broski
我无法理解将响应视为单独的文档或资源如何构成将操作编码到URI中。我提供的选项(如果响应被建模为复杂对象)是Acceptance和Response,而不是AcceptRespond(这将导致将动作与项目混淆)。实际上,在正式的RFQ系统中,响应完全可以单独建模。 - Larry Lustig
我认为REST是资源和表现的结合,而你的模型似乎只使用了资源。如果一个报价的状态(接受/拒绝)不是一种表现形式,那我也不知道什么才是。 - Chris Broski
正如我所说(而且我认为非常清楚),响应可以被视为资源状态的一部分或者是一个独立的资源本身(如果它包含有关响应者、RFQ评审员、条款和报价截止日期等信息)。这个决定应该由要解决的实际问题的陈述来指导,这只有OP才知道。无论如何,两种选择都不必意味着将过程性操作编码到URI中。 - Larry Lustig

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