gpt4 book ai didi

web-services - 针对不同操作的 REST 设计

转载 作者:行者123 更新时间:2023-12-01 23:23:26 25 4
gpt4 key购买 nike

假设我们有一个名为 的实体时间表 .为简单起见,我们假设 时间表实体具有三个属性(TimesheetID、Status 和 Hours)。

我们目前允许的所有者时间表对象提交他们的 时间表通过这个终点——

   POST: /Users/{UserID}/Timesheets/{TimesheetID}

{UserID} 目前是 的所有者时间表目的。使用{TimesheetID},我还可以追溯到 的所有者。时间表在数据库中。

好的,现在问题来了——

现在我们希望他们的经理能够更新 时间表员工对该报告的对象(例如批准/拒绝 时间表 通过更改状态或覆盖小时数)。

但是,有不同级别的管理员权限。一些经理只允许更新状态,而其中一些经理可以同时更新状态和时间。

我是否应该为常规用户提交和管理人员更新(我更喜欢)重用现有的端点?或者我应该为每个“经理级别”创建一个不同的端点?

如果我希望对所有用户提交和管理器更新重复使用现有端点,我如何处理错误,例如管理器配置为仅更新状态,但 时间表发布到 REST 服务的对象的状态和时间都已更改。我是否应该回复带有详细错误描述的 403 以告诉经理您不能更改“时间”属性或更新“状态”并忽略“时间”?

最佳答案

如果您正在关注 HATEOAS constraint ,然后是 GET时间表资源将提供当前可用于与其交互的超媒体控件(链接和表单)。虽然这可以通过多种方式完成,但提供最低耦合形式的方式将包括在其有效参数的形式中。

对于您的示例,我将包含两个具有相同端点的管理器表单 /Users/{UserID}/Timesheets/{TimesheetID} .第一个将具有作为必填字段的状态,第二个将具有状态(状态可以是可选的)和小时(小时是必需的)。

然后你可以让第二种形式回复 403 Forbidden如果提交者不允许提交小时数。或者,您可以过滤包含在 GET 中的表单。 ,以便仅显示用户被允许提交的表单。

更新

例如和 GET/Users/1234/Timesheets/24目前可能产生

<Timesheet>
<TimesheetID>24</TimesheetID>
<Status>Submitted</Status>
<Hours>40</Hours>
</Timesheet>

要应用 HATEOAS 约束,我们需要添加超媒体控件。我们暂时忽略这些 URL,因为它们是实现细节。这可能会给我们一些类似的东西
<Timesheet>
<TimesheetID>24</TimesheetID>
<Status>Submitted</Status>
<Hours>40</Hours>
<link rel="self" href="{{selfUrl}}"/>
<form id="approve" action="{{approveUrl}}" method="PUT">
<Status cardinality="required">
<option value="Approve"/>
</Status>
</form>
<form id="reject" action="{{rejectUrl}}" method="PUT">
<Status cardinality="required">
<option value="Reject"/>
</Status>
</form>
<form id="update" action="{{updateUrl}}" method="PUT">
<Status cardinality="required">
<option value="Approve"/>
<option value="Reject"/>
</Status>
<Hours type="decimal" cardinality="required"/>
</form>

... there might be other forms too, like ...

<form id="cancel" action="{{cancelUrl}}" method="DELETE"/>
</Timesheet>

表单做什么(以及如何识别表单)是在媒体类型中记录的内容。例如:

The cancel form on a Timesheet resource will cancel the timesheet, updating it's status to "Cancelled". Once a Timesheet has been cancelled, it will no longer be possible to update approve or reject the timesheet.



同样在媒体类型中,您将记录资源的属性。例如。,

The Timesheet resource has a the following properties:

  • TimesheetID A unique identifier for the timesheet
  • Status The current status of the timesheet. Status may include
    • Submitted The timesheet has been submitted, but not approved
    • Approved The timesheet has been approved
    • Rejected The timesheet has been rejected
    • Cancelled The timesheet has been cancelled
  • Hours The number of hours (decimal) recorded for the timesheet


虽然这可以由模式指定,但应该避免这样做,因为这样做可能会很难在以后扩展资源。例如,您可能决定添加“WeekEnding”日期属性。现有的调用者不应该关心新的属性,如果他们只是提取他们感兴趣的数据,这很好。但是,如果您在没有考虑扩展的情况下指定了架构,那么添加属性可能会导致调用者中的验证错误添加属性时。

现在,就谁可以做的事情而言,我们有几个选择。一种选择是只包含所有控件并回复 403对于调用者无权调用的任何请求。另一种选择是过滤控件,因此他们只能看到他们可以调用的控件。例如对于可以批准/拒绝的经理,他们可能会得到以下回复
<Timesheet>
<TimesheetID>24</TimesheetID>
<Status>Submitted</Status>
<Hours>40</Hours>
<link rel="self" href="{{selfUrl}}"/>
<form id="approve" action="{{approveUrl}}" method="PUT">
<Status cardinality="required">
<option value="Approve"/>
</Status>
</form>
<form id="reject" action="{{rejectUrl}}" method="PUT">
<Status cardinality="required">
<option value="Reject"/>
</Status>
</form>
</Timesheet>

而可以更新工作时间的经理可能会得到
<Timesheet>
<TimesheetID>24</TimesheetID>
<Status>Submitted</Status>
<Hours>40</Hours>
<link rel="self" href="{{selfUrl}}"/>
<form id="approve" action="{{approveUrl}}" method="PUT">
<Status cardinality="required">
<option value="Approve"/>
</Status>
</form>
<form id="reject" action="{{rejectUrl}}" method="PUT">
<Status cardinality="required">
<option value="Reject"/>
</Status>
</form>
<form id="update" action="{{updateUrl}}" method="PUT">
<Status cardinality="required">
<option value="Approve"/>
<option value="Reject"/>
</Status>
<Hours type="decimal" cardinality="required"/>
</form>
</Timesheet>

或者,您可以包含所有用户的所有表单,但添加一个指示符,表明他们无权调用它。例如对于无法更新时间的经理:
<Timesheet>
<TimesheetID>24</TimesheetID>
<Status>Submitted</Status>
<Hours>40</Hours>
<link rel="self" href="{{selfUrl}}"/>
<form id="approve" action="{{approveUrl}}" method="PUT">
<Status cardinality="required">
<option value="Approve"/>
</Status>
</form>
<form id="reject" action="{{rejectUrl}}" method="PUT">
<Status cardinality="required">
<option value="Reject"/>
</Status>
</form>
<form id="update" action="{{updateUrl}}" method="PUT" authorised="false">
<Status cardinality="required">
<option value="Approve"/>
<option value="Reject"/>
</Status>
<Hours type="decimal" cardinality="required"/>
</form>
</Timesheet>

我更喜欢这种后面的方法,因为您最终不会为您的 API 提供支持调用,开发人员会提示特定的表单不存在。无论哪种方式(包括或过滤),如果调用者调用他们不允许的表单,您仍然会回复 403 .

有点离题,但为了完整性,HATEOAS 真正脱颖而出,因为它根据资源的当前状态传达有效的超媒体控件集。例如,当时间表被取消时,批准/拒绝或更新它不再有效,因此 GET在取消的时间表上可能会返回
<Timesheet>
<TimesheetID>24</TimesheetID>
<Status>Cancelled</Status>
<Hours>40</Hours>
<link rel="self" href="{{selfUrl}}"/>
</Timesheet>

这清楚地向调用者传达了不允许在此特定时间表上执行进一步操作。

您会注意到的另一件事是我们实际上还没有指定任何 URL 或 URL。它们可能都相同(例如 /Users/{UserID}/Timesheets/{TimesheetID} ),也可能不同(例如 selfUrl= /Users/{UserID}/Timesheets/{TimesheetID} 、 updateURL= /Users/{UserID}/Timesheets/{TimesheetID}/update 等)。

最终调用者不应该关心,因为它会使用表单/链接中的任何内容。这为您提供了极大的灵活性,因为您可以更改它们以满足您的实现需求。例如,如果您使用 Command Query Responsibility Segregation (CQRS),发送 GET 可能有意义请求 //readonly.myserver.com/Users/{UserID}/Timesheets/{TimesheetID}POST , PUTDELETE请求 //readwrite.myserver.com/Users/{UserID}/Timesheets/{TimesheetID} .

关于web-services - 针对不同操作的 REST 设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25254873/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com