REST API版本控制(仅对表示进行版本控制,而不是资源本身)

54

我查看了API版本控制的最佳实践?,但对答案还不是很满意。因此,我想通过一个更具体的示例再次询问版本控制部分。我有两个URI(一个包含URI的版本控制,另一个不包含):

http://xxxx/v1/user/123    -> favored solution in discussed thread
http://xxxx/user/123             
我对第一个链接是否表达了REST的意思有所怀疑。我认为 http://xxxx/v1/user/123 这样的链接很令人困惑,因为它暗示着将来可能会有更高的API版本,例如 http://xxxx/v2/user/123。但是从REST的角度来看,这并没有意义,API版本本身已经在HTTP请求中以HTTP 1.0或1.1的形式发送。这种基于REST资源的观点与其他API接口(如SOAP或Java接口)非常不同(在这些接口中,在合格名称中具有API版本是很常见的)。
在REST中,唯一有意义需要进行版本控制的地方是该资源的表示方式(例如,添加或删除新字段)。这种版本控制属于内容协商的一部分,例如:
http://xxx/user/123 + HTTP 'Accept' Header -> Content negotation through header
http://xxx/user/123?v=1                    -> for perma-links/hyperlinks

也可以认为这样的版本内容协商可以成为路径中URI的一部分,但我觉得这是不符合直觉的,因为你最终可能会为同一资源维护不同的URI,并且必须在某些时候保留重定向。

总之,在REST URI中没有API版本控制,只有资源表示的版本控制。表示版本信息属于内容协商(作为查询参数或HTTP“接受”头)。

你认为呢?你在哪些方面不同意/同意?


1
只有一件小事需要补充。对我和使用...v1/风格的唯一障碍是,在你没有控制负载均衡并且无法在前端机器上基于HTTP头定义应用服务器方向时(->内容协商是HTTP头的一部分),这时候通常的标准是使用URL路径。在我能想到的Web框架中,很难基于HTTP头而不是路径在控制器内定义请求映射端点。 - manuel aldana
8个回答

39

我完全同意;一个URI表达了身份,当引入新版本时,身份不会改变。当然,可能会有新的URI表示额外的概念,并且现有的URI可能会重定向...但在URI中包含“v2”对我来说有点像RPC。

请注意,这实际上与REST无关,从REST的角度来看,它只是字符而已。


2
是的,这只是字符。但是拥有漂亮/一致的URI很好,因为它们是API用户编程所针对的接口的一部分。 - manuel aldana
1
这是另一个不正确使用版本控制的URL的好例子,http://blog.steveklabnik.com/2011/07/03/nobody-understands-rest-or-http.html - kidbrax
1
这个问题之前在这里有更详细的回答:https://dev59.com/l3RC5IYBdhLWcg3wK9yV。 - Taras Alenin
URI标识资源,对于内容协商,您应该使用像“Accept”这样的HTTP头。我建议永远不要在资源的URI中使用版本号。 - Carlos Verdes

12

你可以监听一个X-API-Version的 HTTP 请求头。如果这个请求头存在,那么服务器必须使用该版本的 API。如果这个请求头不存在,服务器可以使用最新版本的 API。

> GET /user/123 HTTP/1.1
> Host: xxx
> X-API-Version: >=1.5.1, <2.0.0
> Accept: application/json
>

< HTTP/1.1 200 OK
< X-API-Version: 1.6.12
<
< { "user": { "id": 123, "name": "Bob Smith" } }
<

但是这种方法存在风险,即各种中间代理(代理服务器、缓存)可能会或可能不会遵守该标头,甚至可能不会传递它。 - verveguy
3
你可以使用像“接受”这样的标准标题 :) - gpilotino
头文件何时被移除?由哪些代理移除? - wprl
坏代理/缓存被强制使用的情况时有发生。但是四年后,在大多数情况下,这可能不再是一个真正的问题。 - yfeldblum
@nashape在Cloudfront中不再是一个问题。 - Maroshii
显示剩余2条评论

10

说实话,我和你的看法一致,Manuel。你可以在这个问题《如何给REST URI命名版本号》中看到我的推理。

虽然有很多人持不同意见,但我认为不必太担心。只要你遵守超文本约束,你的URL外观对客户端没有太大影响。


2
+1 "无论您的URL看起来如何,只要您遵守超文本约束,它对客户端的影响并不大。"这一点再怎么强调都不为过。 - Daniel Canas
“只要您遵守超文本约束,您的URL看起来是什么样子对客户端影响并不大。” - andy

3
我同意您不希望在API中呈现的资源URI中出现版本号。这使它们不够“酷”。我也同意,最有可能改变的是表示形式。
但是,这就引出了一个问题,当您更改特定表示形式的内容时会发生什么。例如,如果您原始的JSON表示形式为frobnitz,则:
{"x": "bam", "y": "hello"}

如果你想添加一个“z”字段,你可能会觉得客户端应该知道我们现在使用某种数据方案的第2个版本。
我不确定。我认为你有几个选择:
- 使你的客户端在面对轻微变化的表示时更加灵活。在上面的例子中,我们仍然生成JSON字典。 - 如果必须这样做,在表示本身中放置一个版本(例如此示例中的版本字段)。通过这样做,你实际上声明了JSON内容类型中的子表示。不过我认为这样做相当有限制性。 - 使用自己的MIME类型并对它们进行版本控制:application/x-my-special-json1.0、application/x-my-special-json1.1。这使你能够独立地对表示进行版本控制。同样,使用这种方法会对客户端产生重要的需求,即了解正在发生的事情。
总的来说,我认为你需要优化你的API和表示,以适应那些你没有发明的客户端;其他人发现你的资源后将创建的客户端。即使你正在创建私有的东西,我认为这也很有用,因为它建立了一个有用的设计约束,有助于使你的系统更加健壮。

1
另一种方法可能是说“一个表示具有多个API”:
http://xxx/user/123/api/1.json

如果您愿意,可以在请求时使用最新的API返回表示:

http://xxx/user/123.json

个人而言,我更喜欢其他解决方案,但这是另一种方法,我还没有在这里看到过建议。


1
我发现http://xxxx/v1/user/123很令人困惑,因为它暗示着将来可能会有更高的 API 版本,如http://xxxx/v2/user/123
但它并没有这个意思——不过你未来有这个能力。
但在 REST 术语中,API 的版本本身是 HTTP 1.0 或 1.1,已经包含在 HTTP 请求中了,所以这没有意义。
您的 API 版本和用于发出请求的 HTTP 版本不必相等。
也可以认为这种版本内容协商可以成为路径中 URI 的一部分,但我觉得这很反直觉,因为您可能会最终获得不同的 URI,同时不得不在某些时候维护重定向。
像你展示的那样,将版本作为 URI 参数是可以的。 http://xxx/user/123?v=1 -> 用于永久链接/超链接。

谢谢你的提示。但是我有个问题:就 REST 来说,你认为什么东西构成了一个版本的“YOUR API”?因为除了 HTTP 协议和资源表示之外,我并没有看到 REST 应用程序中的版本。 - manuel aldana
4
通常我会从v1开始构建REST API。API版本基本上是客户端可以达成协议/接口的约定。如果我更改接口/API,并且可能违反了该协议,我可能会增加到v2或v1.1 - 但是,我对版本控制的任何理由都不涉及发出请求的HTTP客户端的版本。这有帮助吗? - mr-sk

0
对于REST,大多数答案都忽略了数据元素。我假设多版本API仍然共享相同的数据层。这意味着数据层强制您以向后兼容的方式进行思考。只有在API以向后兼容的方式更改时,才可能进行必须完成的重大更改。实际上,这意味着在使用API文档中的日期过时标记来指示何时删除某些内容的同时,会在您的实体中静默添加其他属性。理想情况下,您可以使用API密钥用户的电子邮件地址注册方案,以便您可以通知他们在特定范围内(类似Facebook)的过时情况。因此,我认为您不需要在任何地方指定版本。

0
API可以被视为一个顶级资源,/apis/v1/users/,在URI中加入版本号是完全没有问题的。使用Semver时,只有主要版本才会出现在URI中,因为次要版本、补丁等都是向后兼容的。

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