gpt4 book ai didi

rest - 如何处理REST API中新对象属性的引入

转载 作者:行者123 更新时间:2023-12-02 00:54:33 25 4
gpt4 key购买 nike

我有一个 public 的REST API,在所有公开的资源中,公开了以下内容:

POST my/webservice/foos/
PUT my/webservice/foos/{fooId}

两种资源都使用如下的JSON模型:
{
"a": "a mandatory string",
"b": "another mandatory string"
}

因此,用户每次创建新的Foo或更新现有的Foo时,都将必须同时指定新的a和新的b。到现在为止还挺好。

现在,假设Foo实际上具有两个以上的属性,就示例而言,它还具有字符串“c”。该字符串c在其可能的值集中承认为null。显然,该c属性还有其他设置方式。假设可以通过Web界面在现有的Foo上进行设置。

让我们考虑一个具有a =“string a”,b =“string b”和c =“string c”的Foo。
我将我的REST客户端称为该Foo对象上方的PUT函数,而我的Foo的新状态为:a =“新字符串a”,b =“新字符串b”,c =“字符串c”,这很完美精细。

现在,假设已部署对REST API的更新,并且Foo的JSON模型还包括c:
{
"a": "a mandatory string",
"b": "another mandatory string",
"c": "a new optional string"
}

如果使用与以前相同的客户端,并且使用与以前相同的JSON对象调用相同的PUT函数,那么我的Foo将变为:a =“new string a”,b =“new string b”,c = null,其中,从客户的角度来看,是非常意外的。

我完全理解,引入一个非空的新属性将是API中的重大更改,这需要某种版本控制。但是您认为这种情况也应该被视为重大变化吗?我应该拥有仅更新a和b的v1版本,还是更新a,b和c的v2版本吗?有什么最佳实践可以避免增加此类更改的版本号?使用PATCH是切实可行的解决方案吗?

最佳答案

我完全理解,引入一个非空的新属性将是API中的重大更改,这需要某种版本控制。但是您认为这种情况也应该被视为重大变化吗?

仅当您的API基于RPC时。 REST只是Web的一种概括,其设计目的是易于适应变化。 Web上的内容在不断变化而不会破坏客户。 Web如何实现这一目标?

尽管我通常同意Voice的观点,但实际上我对此有一些不同的看法。在REST架构中,服务器或API应该教客户如何做事。客户端应如何知道服务器期望在某个终结点上收到什么? Swagger或类似的文档编制方法主要用于RPC-based APIs,但是REST具有HATEOAS。在这里,超文本用于驱动应用程序状态。这意味着客户将使用收到的响应来探索新的可能性。

Web中使用的传统交互流程是,客户端将调用它感兴趣的URI,并接收响应,该响应被呈现给用户以对其采取进一步的操作。如果需要用户输入,服务器将返回一个Web表单,用户可以在其中输入服务器期望的数据。服务器正是通过这种方式告诉客户端他们必须发送到服务器的内容。当然,服务器将在接收时检查输入,以避免在期望的数字字段上输入字符串文字,并避免输入或验证是否已存在此数据元素。这样,客户端无需事先知道服务器可能期望的数据。如果您引入一个新领域,客户将在收到响应后学习该新领域。

REST体系结构中可能也应该采取类似的方法。要么重用HTML表单,要么使用类似的方法,例如halo+json,或者定义自己的媒体类型,就应该向IANA注册。建议先check for already existing media-types,看看它们是否提供您所需的功能。客户当然需要能够根据接收到的内容动态呈现表单。尽管这里可以使用许多浏览器作为参考,但这可能不是一件容易的事。

尽管HTML格式为do not support PUT , DELETE or PATCH ,但是这些概念仍然可以应用于REST。即在单击“发送”按钮后,客户端通常会以application/x-www-form-urlencoded表示形式将所有输入发送到服务器,除非是client specifies an other enctype ,这取决于服务器将数据转换为将进一步处理的数据,即创建新资源,启动支持过程或调用其他服务。 PUT的定义方式使其可以:


  • 重新配置目标资源以反映新的媒体类型
  • 将PUT表示形式转换为与资源格式一致的格式,然后再将其保存为新的资源状态
  • 用415(不支持的媒体类型)响应拒绝该请求,该响应指示目标资源仅限于其当前媒体类型。


  • 资源的数据如何存储在服务器端通常是与客户端无关的实现细节。它可以简单地存储为包含描述内容的任意键和值的map或dictionary对象,也可以映射为面向对象编程中使用的某些类。此外,它还可以作为纯文本存储在文件中或作为字节blob存储在数据库中。不过,理想情况下,服务器可以将资源状态映射到不同的代表性媒体类型格式,这也使内容类型协商更加占优势,并避免了 typed resources

    服务器的整个概念教导客户它需要什么,并且客户能够根据接收到的数据动态地呈现内容,从而使客户可以动态学习新知识并轻松地适应变化。这实际上是REST体系结构的核心功能,也是为什么很多客户不受其控制的API应当针对这种体系结构的主要卖点之一。

    我应该拥有仅更新a和b的v1版本,还是更新a,b和c的v2版本吗?

    您可能会读到有关 Fielding's take on versioning的信息,它可以简单地归结为: 不要(在URI中保留客户端可见的版本号的意义上)。基本上,如果您遵循上述概述的 server teaches, client learns方法,则除了API所有者自己的版本更改之外,实际上并不需要版本号。 REST体系结构中的客户端无论如何都只会收到最新版本,或者更确切地说,是服务器要向他们公开的版本,并且能够对其进行处理。如果服务器不同时分隔收到的数据,则只有期望服务器上具有特定版本的基于RPC的客户端才能适应这些更改。在这种情况下,通常最好使用通用 namespace 的开关以避免混淆。

    有什么最佳实践可以避免增加此类更改的版本号?

    正如整个帖子所概述的那样,服务器应教客户,而后者应在分析响应时探索他们的机会。这涉及到双方。即使您的服务器遵守REST架构施加的所有约束,客户端也可以解析和分析URI(而不使用链接关系名称),该客户端希望资源返回某些类型,而不是协商双方都理解的表示形式或我不愿意向服务器学习,而是将编程后的带外知识应用到客户端中,这会随着时间的流逝而中断,因此将无法进一步与此类服务器进行交互。

    使用PATCH是切实可行的解决方案吗?

    PATCH通常是一种被误解的HTTP方法。它类似于编程中使用的补丁,其中补丁包含要应用到某些给定代码以将其转换为所需输出的更改。实际上应该通过HTTP执行相同的操作。客户端应采用资源的当前表示形式,并计算将资源状态转换为所需状态所需的更改。通常忽略的修补程序的一个方面是,需要以原子方式应用修补程序。要么应用所有更改,要么不应用任何更改。

    倾向于传统修补的一种媒体类型是 RFC 6902中定义的 application/json-patch+json,它定义了一组指令以应用于被视为JSON对象的资源。通过 JSON Pointers,将在JSON Patch文档中解决当前表示中要更改的各个段。

    RFC 7396中定义了一种不同的修补方法,该方法定义了一种关于如何将更改应用于原始资源的更实际的方法。这由 application/merge-patch+json覆盖。 JSON补丁和JSON合并补丁之间的区别在于,后者不定义要应用于文档的操作,而是在请求中包含整个更新的文档。因此,它依赖于一些固定的规则来应用。即如果新字段出现在请求中,则执行该字段的插入;如果现有字段的值发生更改,则会导致该字段的更新。删除是通过将值设为零来实现的。

    在William Durand的出色博客文章 Please do not patch like an idiot中可以找到有关如何修补的更详尽的解释。

    在实践中,如果您要进行部分更新以执行资源状态,则应使用 PATCH,因为在执行了某些有效性检查之后,定义了 PUT可以用提供的内容替换整个内容。尽管 PUT还包含有关如何通过重叠资源来实现部分更新的提示,但是我猜这种技术并不常见。对于给定的在版本升级时添加新字段的方案,我认为打补丁不是您应该针对的正确方法,而是尝试使用 server teaches, client learns方法作为整个答案的概述。

    关于rest - 如何处理REST API中新对象属性的引入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55311547/

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