【问题标题】:Best way to handle JAX-RS REST API URI versioning处理 JAX-RS REST API URI 版本控制的最佳方式
【发布时间】:2013-01-27 10:40:40
【问题描述】:

我首先在 stackoverflow 中进行了搜索,但找不到与我的问题相关的任何答案。我只能找到与 REST uri 设计相关的问题。

我的问题在后端。 假设我们有两个不同版本的 REST uri

http://api.abc.com/rest/v1/products

http://api.abc.com/rest/v2/products

在后端(服务器端代码)上遵循基于版本的这两组 api 的现有类的正确路由、可管理性和重用的最佳方法是什么?

我已经想到了用不同的@Path 注释定义资源类的方法,例如分别为 v1 和 v2 提供一个包,并在该包的 ProductsResource 类中定义

    package com.abc.api.rest.v1.products;
    @Path("/rest/v1/products")
    public class ProductsResource {...}

    package com.abc.api.rest.v2.products;
    @Path("/rest/v2/products")
    public class ProductsResource {...}

& 然后有基于版本的实现逻辑。这种方法的问题在于,当我们只更改一组 api 中的一个特定资源 api 时,我们还必须将其他类复制到 v2 包中。我们可以避免吗?

如何编写一个自定义注释说@Version & 具有它支持的版本的值?现在无论是v1还是v2,两个请求都会去同一个资源类。

例如说

    package com.abc.api.rest.products;
    @Path("/rest/{version: [0-9]+}/products")
    @Version(1,2)
    public class ProductsResource {...}

更新:

Jarrod 提出了一个 API 版本控制建议来处理标头中的版本。这也是一种方法,但是我期待在我们遵循基于 URI 的版本控制时使用最佳实践

【问题讨论】:

  • 最佳实践是将api版本信息放在URL中
  • 这是一个很好的问题,我对没有回复感到非常惊讶。有数百人反对和支持 URI 版本控制,但所有主要网站都这样做,因为它明确且易于客户使用。 @Deepesh M - 你最后用了什么解决方案?
  • 仅仅因为很多人做错了事情并不意味着它是一个好主意!这只是意味着很多人做错了。
  • 我认为在 URL 上有版本违反了 REST 概念,因为从 REST 角度来看,“v1/users”意味着您正在尝试获取“v1 用户”。一个好的解决方案可能是将它放在请求标头中。
  • 这是一个宗教问题,在您的情况下,“最佳实践”可能是“与您的 API 用户合作最适合您的方法”。这个问题stackoverflow.com/questions/389169/… 概述了一些很好的策略。

标签: java rest jax-rs resteasy


【解决方案1】:

将它放在 URL 中的问题是 URL 应该按位置表示资源。 API 版本不是位置,也不是资源标识符的一部分。

在 URL 中粘贴 /v2/ 会破坏之前的所有现有链接。

有一种指定 API 版本控制的正确方法:

将它放入您想要的 Accept: 标头的 mime-type 中。类似Accept: application/myapp.2.0.1+json

Chain of Responsiblity 模式在这里运行良好,特别是如果有大量不同的 API 版本必须拥有自己的处理程序,这样方法就不会失控。

【讨论】:

  • 继续这一点 - 在 RESTful 应用程序中,您的 URL 应该代表一个资源。该资源可能没有“版本化”;无论访问方式如何,都是一样的。如果资源版本的,它应该在资源中表示,而不是在它的标识符中。
  • 示例:我的用户帐户是 SO 上的资源。网址是:stackoverflow.com/users/322722/cmonkey。当/如果 SO 更改功能、api 或向我的用户帐户添加数据时,该帐户仍然是相同的资源。它不会突然变成 http://.../cmonkey_v2。否则,链接的内容(如此评论)将不再有效。
  • 好的。我想你是说资源保持不变,但版本应该在标题中,以便区分更改前后的 SO。
  • @DeepeshM 你会后悔的,我从经验中知道!
  • 在真正的 RESTful 环境中,版本控制结构的保存位置或 URI 的设计方式并不重要。一个 RESTful 客户端除了它的起始 URI 之外没有关于服务的信息。其他一切都是通过请求和响应来学习的。如果发生版本控制更新,客户端仍会请求与之前可能使用的相同的起始 URI。但是,服务器现在返回不同的东西。如果内容类型保持不变,只有 URI 略有变化,客户端可能甚至不会注意到有很大的不同。
【解决方案2】:

这篇博文有一个示例,说明某些人认为正确的方法,即 URI 中没有版本:http://codebias.blogspot.ca/2014/03/versioning-rest-apis-with-custom-accept.html

简而言之,它利用 JAX-RS @Consume 注释将特定版本的请求与特定实现相关联,例如:

@Consumes({"application/vnd.blog.v1+xml", "application/vnd.blog.v1+json"})

【讨论】:

  • 谁定义了正确的路径? REST 是一种架构风格,没有协议!除非您违反 HTTP 或不遵守 architectural constraints,否则没有对错之分,尽管某些自称 REST 服务在任何方面都不是 RESTful 的,但可能有一些最佳实践,因此应特别小心处理某些声明。跨度>
  • 你说得对,我已经淡化并添加了“by some to be”以强调并非所有人都同意。
【解决方案3】:

我只是想知道为什么没有一个名为 ProductService 的子类

@Path(/v2/ProductService)
ProductServiceV2 extends ProductService {


}


@Path(/v1/ProductService)
 class ProductService{


}

并且仅覆盖 v2 中更改的内容。所有未更改的内容都将与 v1/ProductService 中的相同。

这肯定会导致更多的类,但这是一种更简单的编码方式,只为新版本的 api 中发生的任何变化进行编码,并在不复制代码的情况下恢复到旧版本。

【讨论】:

  • 这也是我最初的想法 :) 我回忆了 EclipseLink 如何以类似的方式实现对 JPA 方言的支持(Oracle11Platform 扩展了 Oracle10Platform 等)。但是后来,我认为 REST 会非常混乱。试想一下,必须在一个又一个版本中扩展您的所有类版本。我喜欢带有 Header 参数的方法。
  • 这太短视了,甚至都不好笑。当您到达v30 时,您将不得不为应用程序中的每个URL 维护30 不同的方法或类?无法缩放。
  • 如果更改很小,我不确定您为什么不能这样做。您只会覆盖已更改的方法。如果您正在添加新方法并弃用旧方法,那么您当时不能选择不同的模式吗?
  • 我们也在考虑扩展类的相同方法。我们正在使用resteasy + swagger。还有哪些其他 apporoches 可用?
猜你喜欢
  • 1970-01-01
  • 2011-01-05
  • 2016-12-22
  • 1970-01-01
  • 2016-02-05
  • 2012-11-16
  • 2011-06-22
  • 2012-09-04
  • 2021-10-08
相关资源
最近更新 更多