REST api 版本控制(仅版本表示,而不是资源本身)
Posted
技术标签:
【中文标题】REST api 版本控制(仅版本表示,而不是资源本身)【英文标题】:REST api versioning (only version the representation, not the resource itself) 【发布时间】:2011-01-02 17:37:48 【问题描述】:我看过Best practices for API versioning?,但不太相信答案,所以我再次用一个更具体的例子来质疑版本控制部分。我有两个 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 1.0 或 1.1,它已经在 HTTP 请求内部发送。这种以 REST 资源为中心的视图与 SOAP 或 Java 接口等其他 api 接口非常不同(在这些接口中,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 版本控制,只有资源表示的版本控制。表示版本信息属于内容协商(如 queryParam 或 HTTP 'Accept')。
你怎么看?您会在哪些方面不同意/同意?
【问题讨论】:
只需添加一点小东西。对我来说唯一使用 ...v1/ 样式的方法是,当您没有控制负载平衡并且无法在前端机器上基于 HTTP 标头定义到应用程序服务器的方向时(->内容协商是 HTTP 标头的一部分)。通常标准是使用 URL 路径。在网络框架中,我认为很难在控制器内部基于 HTTP 标头而不是路径来定义请求映射端点。 【参考方案1】:API 可以被视为***资源,/apis/v1/users/,那么在 URI 中包含版本号绝对没有错。对于 Semver,只有 Major 版本会进入 URI,因为 Minor、Patch 等是向后兼容的。
【讨论】:
【参考方案2】:对于 REST,大多数答案忘记的是数据元素。我假设多个版本的 API 仍然共享同一个数据层。这意味着数据层迫使您以向后兼容的方式思考。只有当您的 API 以向后兼容的方式更改时,才可能进行必须进行的重大更改。实际上,这意味着在您的 API 文档中使用 deprecation by date 来指示何时删除某些内容时,会以静默方式将其他属性添加到您的实体中。理想情况下,您使用带有 API 密钥用户电子邮件地址的注册方案,这样您就可以通知他们在特定范围内弃用(例如 Facebook)。因此,我认为您不需要在任何地方指定版本。
【讨论】:
【参考方案3】:另一种方法可能是说“一个表示具有多个 API”:
http://xxx/user/123/api/1.json
如果您愿意,您可以在这样的请求时使用最新的 API 返回表示:
http://xxx/user/123.json
我个人更喜欢其他解决方案,但这是我还没有看到这里建议的另一种方法。
【讨论】:
【参考方案4】:我同意您不希望在 API 中显示的资源的 URI 中看到版本。这使他们不“酷”。也同意这是最有可能改变的表述。
但是,它确实提出了当您更改特定表示的内容时会发生什么的问题。例如,如果您对 frobnitz 的原始 JSON 表示是
"x": "bam", "y": "hello"
如果您想添加一个“z”字段,您可能会觉得客户应该知道我们现在处于某种数据方案的第 2 版。
我不确定。我认为你有几个选择:
让您的客户灵活地面对轻轻变化的表示形式。在上面的示例中,我们仍在生成 JSON 字典。 如果必须,请在表示本身中放置一个版本(本示例中的版本字段)。通过这样做,您实际上是在 JSON 内容类型中声明了一个子表示。不过我认为这是相当有限的。 使用您自己的 MIME 类型和版本:application/x-my-special-json1.0、application/x-my-special-json1.1。这允许您彼此独立地对表示进行版本控制。同样,通过这个,您对您的客户提出了很大的要求,以了解正在发生的事情。一般来说,我认为您希望优化您的 API 和您尚未自己发明的客户端的表示;其他人会在发现您的资源后创建的内容。我相信即使在您制作私有内容的情况下这也是有用的,因为它构建了一个有用的设计约束,有助于使您的系统更加健壮。
【讨论】:
【参考方案5】:您可以监听X-API-Version
HTTP 请求标头。如果标头存在,则服务器必须使用该版本的 API。如果 header 不存在,服务器可能会使用最新版本的 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"
<
【讨论】:
但这是否会遭受各种中介(代理、缓存)可能会或可能不会尊重该标头并且甚至可能不会通过它的风险? 可能 - 它肯定取决于代理。 在某些情况下删除标题太糟糕了。 你也可以使用像“Accept”这样的标准标题:) 何时删除标题;通过什么代理?【参考方案6】:我完全同意; URI 表示身份,在引入新版本时身份不会改变。当然,可能会有用于其他概念的新 URI,并且现有 URI 可能会重定向……但在 URI 中包含“v2”对我来说是 RPCish。
请注意,这与 REST 毫无关系,真的,因为从 REST 的角度来看,这一切都只是字符。
【讨论】:
是的,它只是字符。但最好有好的/一致的 URI,因为它们是 api 用户程序所针对的接口的一部分。 这里是另一个很好的例子,说明如何不在你的 url 中使用版本控制,blog.steveklabnik.com/2011/07/03/… 这在***.com/q/389169/104261之前已经得到了更详细的回答。 URI 标识资源,对于内容协商,您应该使用 http 标头,例如“Accept”。我建议不要在资源的 uri 中使用版本号。【参考方案7】:对于它的价值,我同意曼努埃尔的观点。你可以在这个问题中看到我的推理How to version REST URIs
似乎有很多人不同意,但我不会担心。只要您尊重超文本约束,您的 url 看起来对您的客户没有太大影响。
【讨论】:
+1 “只要您遵守超文本约束,您的 url 看起来对您的客户没有太大影响”。这怎么强调都不过分。 +1 表示“只要您遵守超文本约束,您的 url 看起来真的不会对您的客户产生太大影响”【参考方案8】:我找到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 术语而言,您认为是什么构成了“您的 API”版本?因为除了 HTTP 协议和资源表示之外,我只是看不到 REST 应用程序中的版本。 我通常在 v1 开始使用 REST API。 api 版本基本上是客户可以同意的合同/接口。如果我更改接口/api,并且也许我违反了该合同,我可能会增加到 v2 或 v1.1 - 但是,我对版本控制的推理都没有反映发出请求的 HTTP 客户端的版本。这有帮助吗?以上是关于REST api 版本控制(仅版本表示,而不是资源本身)的主要内容,如果未能解决你的问题,请参考以下文章
使用 Guice 3.0 + JaxRS 2.0 对 REST API 进行版本控制