【问题标题】:REST Design - PATCH for a specific updateREST 设计 - 特定更新的补丁
【发布时间】:2014-11-13 11:25:15
【问题描述】:

我有一个代表一组物理设备的资源。

调用GET v1/devices/ 会产生以下结果:

[
  {
    "MacAddress": "DD-22-33-15-15-66",
    "Name": "Test Device",
    "State": "Approved"
  },
  {
    "MacAddress": "E5-21-56-44-11-B6",
    "CompanyId": "Another Test Device",
    "State": "Pending"
  }
]

设备的状态(一个重要属性)只能是pendingapproved,因此也可以使用以下 GET 请求:

  • GET v1/devices/pending :检索所有挂起的设备
  • GET v1/devices/approved :检索所有已批准的设备

您还可以使用GET v1/devices/EF-55-33-44-54-61从资源中获取单个设备

我现在希望能够仅将设备状态从 Pending 更新为 Approved

下面的 PATCH 调用有意义吗?

PATCH v1/devices/EF-55-33-44-54-61/approve

从一些阅读来看,正确的做法似乎是这样的:

[
  {"replace": "/state", "value": "approved"}
]

但是对于这样一个特定的更新来说,这似乎太灵活了。我从不希望更新其他值,也不希望以任何其他方式更改状态。

【问题讨论】:

    标签: rest asp.net-web-api patch asp.net-web-api2 restful-url


    【解决方案1】:

    首先,您不应使用此 URL 模式来过滤返回的设备。您正在使用下面的模式,但恕我直言,这不是最好的方法。

    GET v1/devices/<approved|pending>
    

    相反,如果您想使用作为过滤器的一个或多个属性来过滤或限制返回的设备,您应该像这样使用 QueryString:

    GET v1/devices?status=<approved|pending>
    

    请记住,您的 URI 应该只针对资源,在我看来,“已批准”不是资源。实际上,这是该资源的一种状态。在这种情况下,请使用 QueryString 过滤或限制您想要处理的资源。继续您的主要问题...

    下面的 PATCH 调用有意义吗? PATCH v1/devices/EF-55-33-44-54-61/approve

    是的,不是的! :) 您可以使用与 Github 相同的方法。在 Github 上,如果你想通过他们的 API “star”一个 gist,你必须在子资源“/gist/id/star”上做一个 POST。您可能会将其视为“我正在创建子资源之星”。如果你想“取消星标”这个要点,你必须在“/gist/id/star”上做一个删除(我正在从主资源中删除子资源星号)。

    在你的情况下,我会做这样的事情:

    POST    v1/devices/EF-55-33-44-54-61/approval
    

    您可以将其视为“我正在创建子资源审批”。也许它会有不同的状态,比如“批准日期”、“批准它的用户”......

    DELETE v1/devices/EF-55-33-44-54-61/approval
    

    你可能会认为它是“我正在删除子资源”批准,现在这个设备不再有这个关联了。

    【讨论】:

    • 很好的答案。问题:当 PATCH 用于部分更新时,为什么在这种情况下 PUT over PATCH?
    • 我的错!首先,我必须编辑我的答案,因为我写的是 PUT 而不是 POST。现在你的问题变成了“为什么 POST over PATCH”。 :) 因为您正在创建资源“批准”而不是更新属性“状态”。
    • 考虑到你不应该通过你的 REST API 暴露你的底层架构。这是两个不同的东西。您正在使用一种 OO 语言/体系结构来开发您的系统,但是您正在通过 Web 公开它,因此您必须从资源的角度来考虑它。我不知道我是否说清楚了。 :P
    【解决方案2】:

    我会针对正在修补的对象的 URI 执行 PATCH:

    PATCH v1/devices/EF-55-33-44-54-61
    

    没有约定说 Web API 控制器中的 PATCH 方法必须允许修补设备对象上的所有属性。如果您只支持对该属性的更新,那也没关系。

    虽然上面较长的 JSON 似乎有点过分,但这也意味着如果稍后发生变化并且您确实需要修补多个属性,您现在只需更新控制器以允许它并且 JSON 保持不变(尽管可能会在一次调用中完成多个替换)。

    【讨论】:

    • 不同之处在于我没有使用来自用户的字符串值更新state 属性。用户只是想“批准”一个设备(恰好将属性更改为approved)。甚至不需要国有财产,但我仍然希望设备获得批准。如果我想允许对属性进行补丁更新,那么我将打开一个典型的 PATCH 方法。
    • 如果您要制作特殊的端点,例如“PATCH v1/devices/EF-55-33-44-54-61/approve”,我只会将有效负载留空并让端点的控制器方法只是更改标志而不查看任何输入。这是一个方便的端点,只有在您想要实现典型的 PATCH 方法时,您在 OP 中包含的通用补丁 JSON 才有意义。我的回答主要是解决你想要修补其他成员的(不太可能的)未来。
    • 是的,这确实是一种方便的方法,控制器不考虑输入。输入纯粹用于路由目的。
    【解决方案3】:

    下面的 PATCH 调用有意义吗?

    PATCH v1/devices/EF-55-33-44-54-61/approve

    REST 不是 RPC,因此您不应将操作绑定到 URI,而应将它们绑定到方法(动词)- URI(名词)对。因此,在您的情况下,/approve 动词不是 RESTish,您的请求正文也不是 RESTish,因为它包含命令而不是资源表示。

    我会改用类似的东西:

    • PUT v1/devices/EF-55-33-44-54-61/state "approved"
    • PATCH v1/devices/EF-55-33-44-54-61 {state: "approved"}

    请注意,真正的 REST 客户端遵循超链接,而不是构建例如 URI。这就是(在很多方面)使它们松散耦合的原因。

    【讨论】:

      猜你喜欢
      • 2021-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多