ChatGPT解决这个技术问题 Extra ChatGPT

如何对 REST URI 进行版本控制

版本 REST URI 的最佳方式是什么?目前,我们在 URI 本身中有一个版本号,即。

http://example.com/users/v4/1234/

对于此表示的第 4 版。

版本是否属于查询字符串? IE。

http://example.com/users/1234?version=4

还是以另一种方式最好地完成版本控制?


M
Matt Hamilton

不要版本 URL,因为...

你打破了固定链接

网址更改将像疾病一样通过您的界面传播。您如何处理未更改但指向已更改的表示的表示?如果您更改网址,则会破坏旧客户。如果您留下网址,您的新客户可能无法使用。

版本控制媒体类型是一种更加灵活的解决方案。

假设您的资源正在返回 application/vnd.yourcompany.user+xml 的某些变体,您需要做的就是创建对新的 application/vnd.yourcompany.userV2+xml 媒体类型的支持,并通过内容协商的魔力您的 v1 和v2 客户端可以和平共处。

在 RESTful 接口中,最接近合同的是定义在客户端和服务器之间交换的媒体类型。

客户端用来与服务器交互的 URL 应该由嵌入在先前检索到的表示中的服务器提供。客户端需要知道的唯一 URL 是接口的根 URL。仅当您在客户端上构建 url 时,向 url 添加版本号才有价值,您不应该使用 RESTful 接口来执行此操作。

如果您需要更改会破坏现有客户的媒体类型,请创建一个新客户并保留您的网址!

对于那些目前说如果我使用 application/xml 和 application/json 作为媒体类型的读者来说这是没有意义的。我们应该如何对它们进行版本控制?你不是。这些媒体类型对于 RESTful 接口几乎没有用,除非您使用代码下载来解析它们,此时版本控制是一个有争议的问题。


解决要点。 1. 您不会破坏永久链接,因为永久链接链接到特定版本 2. 如果所有内容都已版本化,这不是问题。旧网址仍然可以使用。理想情况下,您不希望版本 4 URL 返回与版本 3 资源的关联。 3. 也许
想象一下,如果当您升级到新版本的网络浏览器时,您收藏的所有收藏夹都损坏了!请记住,从概念上讲,用户正在保存指向资源的链接,而不是资源表示的版本。
@Gili为了满足REST api自描述的要求,内容类型标头必须提供消息的完整语义描述。换句话说,您的媒体类型就是您的数据合同。如果您交付 application/xml 或 application/json,您将不会告诉客户端该 XML/Json 中包含的内容。客户端应用程序在拉出 /Customer/Name 的那一刻,您正在创建基于不在消息中的信息的耦合。消除带外耦合对于实现 RESTful 至关重要。
@Gili 除了根 URL 之外,客户端应该不知道 API 的 URL。您不应将表示格式与特定的 URL 联系起来。在选择媒体类型时,您确实需要在特定格式(如 application/vnd.mycompany.myformat+xml)或标准化格式(如 XHtml、Atom、RDF 等)之间进行选择。
将 API 版本作为单独的标头字段是否有意义?像这样:接受:application/com.example.myapp+json;版本=1.0
Z
Zef Hemel

我想说让它成为 URI 本身的一部分(选项 1)是最好的,因为 v4 标识的资源与 v3 不同。第二个选项中的查询参数最好用于传入与请求相关的附加(查询)信息,而不是资源。


问题是,我们讨论的是不同的资源吗?还是该资源的不同表示? REST 是否区分表示和资源?
@Cheeso - OP表明它是不同的表示而不是不同的资源,因此我的回答。
在此处stackoverflow.com/q/389169/104261之前已对此进行了更详细的回答
+1 表示“第二个选项中的查询参数最适合用于传入与请求相关的附加(查询)信息,而不是资源”
对于不同的表示,我认为您应该使用“接受”之类的标头,然后客户端可以向服务器指定“我只接受版本 4”,服务器可以使用该表示进行回答。如果未发送接受,则提供最后一个版本。
R
Ruben Bartelink

啊,我又要戴上我那顶脾气暴躁的旧帽子了。

从 ReST 的角度来看,这根本不重要。不是香肠。

客户端接收到它想要遵循的 URI,并将其视为不透明的字符串。将您想要的任何内容放入其中,客户端不知道其上的版本标识符之类的东西。

客户知道的是它可以处理媒体类型,我建议听从 Darrel 的建议。此外,我个人认为,需要将 restful 架构中使用的格式更改 4 次应该会带来巨大的警告信号,表明您正在做一些严重错误的事情,并且完全绕过了为更改弹性设计媒体类型的需要。

但无论哪种方式,客户端都只能处理具有它可以理解的格式的文档,并按照其中的链接进行操作。它应该知道链接关系(转换)。所以 URI 中的内容完全无关紧要。

我个人会投票给http://localhost/3f3405d5-5984-4683-bf26-aca186d21c04

一个完全有效的标识符,可以防止任何进一步的客户端开发人员或接触系统的人质疑是否应该将 v4 放在 URI 的开头或结尾(我建议,从服务器的角度来看,你不应该有 4版本,但有 4 种媒体类型)。


如果表示需要显着改变并且不能向后兼容怎么办?
通过以可扩展的方式设计媒体类型,例如使用命名空间和可扩展的 xsd,或现有的 xml 格式(如 atom),这应该是可以预防的。如果你真的必须这样做,另一种媒体类型就是你要走的路。
我喜欢这个完全有效的答案,但我认为建议的 URI 更能说明这一点,而不是你确实想要“可破解”URI 的真实场景。
C
Community

您不应该将版本放在 URL 中,而应该将版本放在请求的 Accept Header 中 - 请参阅我在此线程上的帖子:

Best practices for API versioning?

如果您开始在 URL 中粘贴版本,您最终会得到如下愚蠢的 URL:http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

还有许多其他问题也随之而来 - 请参阅我的博客:http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html


抱歉,但我认为您最终不会得到像这样愚蠢的 URL。您将版本号绑定到特定资源或(更糟)特定表示。那太傻了,IMO。相反,您正在对 API 进行版本控制,因此 URI 中永远不会有多个版本。
C
Community

这些(不太具体的)关于 REST API 版本控制的 SO 问题可能会有所帮助:

版本控制 RESTful 服务?

Web 服务 REST API 版本控制的最佳实践


J
Javier C.

有 4 种不同的 API 版本控制方法:

将版本添加到 URI 路径:http://example.com/api/v1/foo http://example.com/api/v2/foo 当您有重大更改时,您必须增加版本,例如:v1、v2、 v3... 你可以在你的代码中实现一个控制器,如下所示: @RestController public class FooVersioningController { @GetMapping("v1/foo") public FooV1 fooV1() { return new FooV1("firstname lastname"); } @GetMapping("v2/foo") public FooV2 fooV2() { return new FooV2(new Name("firstname", "lastname")); }

请求参数版本控制: http://example.com/api/v2/foo/param?version=1 http://example.com/api/v2/foo/param?version=2 版本参数可以是可选的或必需的取决于您希望如何使用 API。实现可能类似于:@GetMapping(value = "/foo/param", params = "version=1") public FooV1 paramV1() { return new FooV1("firstname lastname"); } @GetMapping(value = "/foo/param", params = "version=2") public FooV2 paramV2() { return new FooV2(new Name("firstname", "lastname")); }

传递自定义标头:http://localhost:8080/foo/produces 带有标头:标头[Accept=application/vnd.company.app-v1+json] 或:标头[Accept=application/vnd.company.app-v2 +json] 这种方案的最大优势主要是语义:您不会因为与版本控制有关的任何事情而使 URI 变得混乱。可能的实现:@GetMapping(value = "/foo/produces",produces = "application/vnd.company.app-v1+json") public FooV1 producerV1() { return new FooV1("firstname lastname"); } @GetMapping(value = "/foo/produces",produces = "application/vnd.company.app-v2+json") public FooV2produceV2() { return new FooV2(new Name("firstname", "lastname") ); }

更改主机名或使用 API 网关:本质上,您是将 API 从一个主机名移动到另一个主机名。您甚至可以将其称为为相同资源构建新 API。此外,您可以使用 API 网关执行此操作。


A
Asma Zubair

我想创建版本化的 API,我发现这篇文章非常有用:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

关于“我希望对我的 API 进行版本控制”有一小部分。我发现它简单易懂。关键是使用 header 中的 Accept 字段来传递版本信息。


U
UberSteve

如果 REST 服务需要在使用前进行身份验证,您可以轻松地将 API 密钥/令牌与 API 版本相关联并在内部进行路由。要使用新版本的 API,可能需要新的 API 密钥,并链接到该版本。

不幸的是,此解决方案仅适用于基于身份验证的 API。但是,它确实将版本保留在 URI 之外。


i
inf3rno

如果您使用 URI 进行版本控制,那么版本号应该在 API 根的 URI 中,因此每个资源标识符都可以包含它。

从技术上讲,REST API 不会因 URL 更改(统一接口约束的结果)而中断。仅当相关语义(例如特定于 API 的 RDF 词汇)以非向后兼容的方式(罕见)更改时,它才会中断。目前,很多人不使用导航链接(HATEOAS 约束)和词汇来注释他们的 REST 响应(自我描述消息约束),这就是他们的客户中断的原因。

自定义 MIME 类型和 MIME 类型版本控制没有帮助,因为将相关元数据和表示的结构放入短字符串中不起作用。办公室。元数据和结构会经常更改,因此版本号也会...

因此,要回答您的问题,最好的方法是使用词汇(Hydralinked data)注释您的请求和响应并忘记版本控制或仅通过不向后兼容的词汇更改使用它(例如,如果您想用另一个词汇替换一)。


P
Paul Morgan

我会将版本作为可选值包含在 URI 的末尾。这可以是 /V4 之类的后缀,也可以是您描述的查询参数。您甚至可以将 /V4 重定向到查询参数,以便支持这两种变体。


Y
Yarco

我投票赞成在 mime 类型中这样做,而不是在 URL 中。但原因和其他人不一样。

我认为用于定位唯一资源的 URL 应该是唯一的(那些重定向除外)。那么,如果您在 URL 中接受 /v2.0,为什么它不是 /ver2.0/v2//v2.0.0?甚至是 -alpha-beta? (那么它就完全变成了semver的概念)

因此,mime 类型的版本比 URL 更容易接受。